25 March 2017

[Android Code] มาสำรวจกันว่ามีคำสั่งสำคัญอะไรบ้างที่เปลี่ยนแปลงไปบน Android O



        หลังจากที่ Android O เปิดตัวมาได้ซักพัก ก็ได้มีบทความเกี่ยวกับการเปลี่ยนแปลงในเวอร์ชันใหม่นี้ไปแล้ว ซึ่งเจ้าของบล็อกก็ไม่มีเหตุผลว่าทำไมต้องไปเขียนซ้ำกับคนอื่นอีก ก็เลยหนีมาเขียนเรื่องนี้แทน เพื่อให้ผู้ที่หลงเข้ามาอ่านได้รู้ว่านอกจากฟีเจอร์หลักๆแล้ว มีคำสั่งอะไรบ้างที่เปลี่ยนแปลงไป

ก่อนที่จะอ่านต่อ

        ถ้ายังไม่รู้ว่าฟีเจอร์หลักของ Android O มีอะไรบ้างที่มีผลต่อนักพัฒนา ให้ไปอ่านจากบทความเพื่อนบ้านก่อนเลย

        • มาดูกันว่า Android O มีอะไรใหม่บ้าง ในแบบฉบับนักพัฒนา
        • สรุปของเล่นใหม่คร่าวๆใน ANDROID O(MG) ฉบับ DEVELOPER

ป่ะ ลุยกันต่อ

         นอกจากจะมีฟีเจอร์ใหม่ๆ และคำสั่งใหม่ๆสำหรับฟีเจอร์เหล่านั้นแล้ว ก็ยังมีการเปลี่ยนแปลงของโค้ดใน API เก่าที่มีอยู่ด้วย ซึ่งเจ้าของบล็อกเข้าไปนั่งไล่ส่องมาแล้ว ก็เลยขอหยิบเฉพาะคำสั่งของคลาสที่สำคัญๆมาให้ดูนะ


Configuration

        ชื่อเต็ม :  android.content.res.Configuration

        มีการเพิ่มคำสั่งเข้ามาเพื่อให้รองรับการแสดงผลบนหน้าจอที่รองรับ HDR และ Wide Color Gamut

boolean isScreenHdr = getResources().getConfiguration().isScreenHdr();
boolean isScreenWideColorGamut = getResources().getConfiguration().isScreenWideColorGamut();

        ซึ่งถ้าลงละเอียดไปลึกกว่านั้น ผู้ที่หลงเข้ามาอ่านสามารถดึงค่า Color Mode ด้วยคำสั่งแบบนี้ได้เลย

int colorMode = getResources().getConfiguration().colorMode;

        แล้วอยากรู้ว่ามีค่าเป็นอะไรบ้างก็ให้ทำ Bit Mask กับค่า Color Mode ที่ได้ฮะ

Configuration.COLOR_MODE_HDR_MASK    
Configuration.COLOR_MODE_HDR_NO     
Configuration.COLOR_MODE_HDR_SHIFT     
Configuration.COLOR_MODE_HDR_UNDEFINED     
Configuration.COLOR_MODE_HDR_YES     
Configuration.COLOR_MODE_UNDEFINED     
Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK     
Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO     
Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED     
Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES

        แต่แนะนำว่าอย่าไปทำครับ เหนื่อยตายกันพอดี ไปเช็คจากคำสั่งที่มีให้โดยตรงเถอะนะ..

        และมีการเพิ่ม UI Mode สำหรับ VR Headset ให้ด้วย สำหรับนักพัฒนาที่ทำเกี่ยวกับ VR ก็สามารถ Detected ได้จาก Configuration แล้วว่าแสดงผลหน้าจอสำหรับ VR อยู่หรือไม่

int Configuration.UI_MODE_TYPE_VR_HEADSET


Display

        ชื่อเต็ม : android.view.Display

        เพิ่มคำสั่งสำหรับเช็คว่าหน้าจอที่แสดงผลอยู่นั้นเป็น HDR หรือ Wide Color Gamut หรือป่าว

boolean isHdr = getWindowManager().getDefaultDisplay().isHdr();
boolean isWideColorGamut = getWindowManager().getDefaultDisplay().isWideColorGamut();


Window

        ชื่อเต็ม : android.view.Window

        เพิ่มคำสั่งสำหรับดึงค่า Color Mode ของหน้าจอ

int colorMode = getWindow().getColorMode();


Permission

        ชื่อเต็ม :  android.Manifest.permission

        เพิ่ม Permission เข้ามาใหม่อีก 10 ตัว

Manifest.permission.ALLOCATE_AGGRESSIVE     
Manifest.permission.BIND_AUTO_FILL     
Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE     
Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE     
Manifest.permission.MANAGE_OWN_CALLS     
Manifest.permission.READ_PHONE_NUMBER     
Manifest.permission.REQUEST_DELETE_PACKAGES     
Manifest.permission.RESTRICTED_VR_ACCESS     
Manifest.permission.RUN_IN_BACKGROUND     
Manifest.permission.USE_DATA_IN_BACKGROUND

        ALLOCATE_AGGRESSIVE เป็นการบังคับจองพื้นที่ข้อมูล ใช้สำหรับ System เท่านั้น ไม่สามารถเข้าไปใช้งานได้

        BIND_AUTO_FILL สำหรับใช้งาน Auto Fill ที่เพิ่มเข้ามาใหม่ในเวอร์ชันนี้

        BIND_VISUAL_VOICEMAIL_SERVICE สำหรับใช้งาน Visual Voicemail ที่เพิ่มเข้ามาใหม่ในเวอร์ชันนี้ (สามารถ)

        INSTANT_APP_FOREGROUND_SERVICE เพื่อให้ Instant App สามารถทำงานเป็น Foreground Service ได้

        MANAGE_OWN_CALLS สำหรับควบคุมการรับสายผ่านโค้ด (เวอร์ชันนี้ยอมให้ทำแบบนี้ได้แล้ว)

        READ_PHONE_NUMBER ใช้ในการขอดึงเบอร์มือถือจากเครื่อง

        REQUEST_DELETE_PACKAGES เพื่อให้สามารถเรียกหน้าต่างลบแอปฯขึ้นมาด้วยคำสั่ง ACTION_UNINSTALL_PACKAGE (ใช้ใน Intent) ซึ่งมีตั้งแต่สมัย API 14 แล้วล่ะ แต่ใน API 26 เป็นต้นไปจะต้องประกาศ Permission นี้ด้วยทุกครั้ง

        RESTRICTED_VR_ACCESS เข้าถึง VR API บางอย่างที่ถูกจำกัดสิทธิ์ไว้ Permission นี้สำหรับ System App เท่านั้น

        RUN_IN_BACKGROUND เพื่อให้แอปฯสามารถทำงานใน Background ได้ (ใช้ใน CompanionDeviceManager สำหรับ System App เท่านั้น)

        USE_DATA_IN_BACKGROUND เพื่อให้แอปฯสามารถใช้งานอินเตอร์เน็ตใน Background ได้ (ใช้ใน CompanionDeviceManager สำหรับ System App เท่านั้น)


Fingerprint Gesture

        เป็นหนึ่งฟีเจอร์ที่อยู่ใน Accessibility หรือโหมดคนพิการ ซึ่งเปิดให้นักพัฒนาสามารถดัก Event การ Gesture บน Fingerprint ได้

        ถ้านึกไม่ออก ให้นึกถึง Google Pixel ที่ Fingerprint สามารถทำ Gesture ง่ายๆได้ เช่นปัดลงเพื่อเปิด Notification นั่นล่ะครับ ที่สามารถทำบน Android O ได้เลย แต่ก็ขึ้นอยู่กับแต่ละเครื่องด้วยว่า Fingerprint มีมั้ย และรองรับการทำ Gesture มั้ย

        โดยจะมีคลาสสำคัญที่ชื่อว่า FingerprintGestureController ที่ใช้ในการ Register/Unregister Event Callback และลักษณะของ Gesture ก็จะมีอยู่ 4 แบบเท่านั้น คือ ปัดขึ้น, ปัดลง, ปัดซ้าย และปัดขวา

FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN
FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT
FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT
FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP


Animator

        เป็นหนึ่งใน Animation API ที่ใช้งานกันอยู่บ่อยๆ ในตอนนี้มีการเพิ่มคำสั่งให้กำหนดระยะเวลาที่จะเริ่มเล่นได้ เช่น ใช้ Animator เลื่อนจากตำแหน่ง X : 0 ไปยังตำแหน่ง X : 100 โดยใช้เวลาทั้งหมด  1,000 มิลลิวินาทีหรือ 1 วินาที

ObjectAnimator animator = ObjectAnimator.ofFloat(tvMessage, View.TRANSLATION_X, 0, 100);
animator.setDuration(1000);
animator.start();

        ระหว่างที่ Animator กำลังเล่นอยู่นั้น มีคำสั่งสำหรับเช็คได้ว่าตอนนี้กำลังเล่นอยู่ที่วินาทีเท่าไร และกำหนดได้ว่าจะให้ข้ามไปแสดงผลเป็นวินาทีที่เท่าไร (เหมือนเวลาดูหนังแล้วสั่งให้ข้ามไปเล่นวินาทีที่ต้องการ)

long currentPlayTime = animator.getCurrentPlayTime();
animator.setCurrentPlayTime(500);

        จากคำสั่ง setCurrentPlayTime จะทำให้ Animator ข้ามไปแสดงผลที่ 500 มิลลิวินาทีหรือ 0.5 วินาที ทันที

        และสามารถสั่งให้เล่นย้อนกลับได้ด้วยนะเออ

animator.reverse();

        ซึ่งคำสั่งสำหรับ Current Play Time และ Reverese นั้นจะใช้ได้ก็ต่อเมื่อ Animator ทำงานแล้วเท่านั้นนะจ๊ะ

        เมื่อ Animator สามารถทำงานแบบ Reverse ได้ ดังนั้น Animator Listener จึงเพิ่ม Event เข้ามาอีก 2 ตัวเพื่อรองรับการทำงานแบบ Reverse

animator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation, boolean isReverse) {
        
    }

    @Override
    public void onAnimationEnd(Animator animation, boolean isReverse) {

    }
    
    ...
});

        หมายเหตุ - แนะนำให้ใช้ AnimatorListenerAdapter แทน Animator.AnimatorListener


Fragment

        สำหรับ Fragment Manager ในตอนนี้มี Fragment Life Cycle Callback ให้ใช้แล้วจ้าาาาาาาาาาาาาาา (Support v4 ก็มีให้ใช้แล้วเหมือนกัน)

private FragmentManager.FragmentLifecycleCallbacks callback = new FragmentManager.FragmentLifecycleCallbacks() {
    @Override
    public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {
     
    }

    @Override
    public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {
     
    }

    @Override
    public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
     
    }

    @Override
    public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
     
    }

    @Override
    public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {
     
    }

    @Override
    public void onFragmentStarted(FragmentManager fm, Fragment f) {
     
    }

    @Override
    public void onFragmentResumed(FragmentManager fm, Fragment f) {
     
    }

    @Override
    public void onFragmentPaused(FragmentManager fm, Fragment f) {
     
    }

    @Override
    public void onFragmentStopped(FragmentManager fm, Fragment f) {
     
    }

    @Override
    public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {
     
    }

    @Override
    public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
  
    }

    @Override
    public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
  
    }

    @Override
    public void onFragmentDetached(FragmentManager fm, Fragment f) {
  
    }
};

        ซึ่ง Callback ตัวนี้จะบอกให้หมดว่า Fragment ตัวไหนเกิด Life Cycle อะไร ส่วนการใช้งานก็จะเป็นลักษณะของ Register/Unregister

FragmentManager manager = getSupportFragmentManager();
manager.registerFragmentLifecycleCallbacks(callback, true);

// เมื่อไม่ใช้งานแล้ว
manager.unregisterFragmentLifecycleCallbacks(callback);

        คำสั่ง Register นั้นสามารถกำหนดได้ว่าจะให้คำสั่งนี้ Recursive ใน Child Fragment Manager ด้วยหรือไม่

        ส่วน Fragment นั้นก็มีการเพิ่มคำสั่งเข้ามาเล็กน้อย

boolean isStateSaved()     
void postponeEnterTransition()     
void startPostponedEnterTransition()

        isStateSaved เช็คว่า Fragment State มีการ Save เก็บไว้เรียบร้อยแล้วหรือยัง

        postponeEnterTransition กำหนดให้ Transition ของ Fragment ตัวนั้นอย่าเพิ่งทำงาน จนกว่าจะสั่งด้วยคำสั่ง startPostponedEnterTransition (มี Transition แต่ยังไม่อยากให้เล่นทันที)

        startPostponedEnterTransition สั่งให้ Transition ของ Fragment ที่กำหนดด้วยคำสั่ง postponeEnterTransition เริ่มทำงาน


Notification

        เพิ่มคำสั่งเพื่อรองรับ Channel และ Channel Group ซึ่งเจ้าของบล็อกแยกเป็นบทความเรื่องนี้ให้แล้ว สามารถตามไปอ่านกันได้ที่ [Android Code] ลองเล่น Notification Channels ของเล่นใหม่จาก Android O


Activity

        สำหรับคลาส Activity จะเพิ่มคำสั่งต่างๆเพื่อให้รองรับการทำงานแบบ Picture in Picture และ Freeform Window

boolean enterPictureInPictureMode(PictureInPictureArgs arge)    
boolean isOverlayWithDecorCaptionEnabled()     
void setOverlayWithDecorCaptionEnabled(boolean enabled)     
void setPictureInPictureArgs(PictureInPictureArgs args)

        เนื่องจาก Android O รองรับการแสดงผลบนหลายหน้าจอ (Multi-display Support) จึงมีการเพิ่ม Override Method ที่ชื่อว่า onMovedToDisplay เข้ามาด้วย

public class MainActivity extends AppCompatActivity {
    
    ...

    @Override
    public void onMovedToDisplay(int displayId) {
        
    }
}


        ซึ่ง onMovedToDisplay จะทำให้นักพัฒนารู้ได้ว่าในขณะที่ Activity นั้นๆกำลังทำงานอยู่แล้วผู้ใช้มีการย้ายไปแสดงบนหน้าจออื่น

        และมีการเพิ่มคำสั่ง isActivityTransitionRunning เพื่อให้เช็คได้ว่า Activity ณ ตอนนั้นกำลังแสดง Transition ใดๆอยู่หรือป่าว

boolean isActivityTransitionRunning()


AppWidgetManager

        ชื่อเต็ม : android.appwidget.AppWidgetManager

        เพิ่มคำสั่งสำหรับ App Widget Pinning เพราะในเวอร์ชันนี้ Widget ก็สามารถทำ Pinning ได้แล้วเหมือนกัน

boolean isRequestPinAppWidgetSupported()     
boolean requestPinAppWidget(ComponentName provider, PendingIntent successCallback)


Content Provider

        มีการ Overload Method เพิ่มเข้ามาให้กับ Query และ Refresh เพื่อให้รองรับการยกเลิกกลางคันโดยใช้คลาส CancellationSignal

CancellationSignal signal = new CancellationSignal();

// ใช้กับ Query
provider.query(uri, projection, queryArgs, signal);

// ใช้กับ Refresh
provider.refresh(uri, args, signal);

// เมื่อต้องการยกเลิกการ Query หรือ Refresh กลางคัน
signal.cancel();


Context

        มีการเพิ่ม Service Manager เข้ามาใหม่ทั้งหมด 5 ตัวด้วยกัน (บางอันใช้สำหรับ System App เท่านั้น)

Context.COMPANION_DEVICE_SERVICE     
Context.FONT_SERVICE     
Context.STORAGE_STATS_SERVICE     
Context.TEXT_CLASSIFICATION_SERVICE     
Context.WIFI_AWARE_SERVICE

// ยกตัวอย่างการเรียกใช้ Font Manager
FontManager fontManager = (FontManager) getSystemService(Context.FONT_SERVICE);

        และเพิ่มคำสั่งเข้ามาอีก 1 ตัว

Context createContextForSplit(String splitName)

        เป็นคำสั่งที่ใช้สำหรับสร้าง Context ขึ้นมาใหม่โดยใช้ Context ที่มีอยู่ ซึ่ง Context ที่ถูกสร้างขึ้นมาใหม่จะเป็นคนละตัวกับ Context ตัวเก่า แต่ก็ได้ Resource จากตัวเก่ามาใช้งานได้อยู่นะ


Intent

        เพิ่มคำสั่ง removeFlags มาให้ จะได้ลบ Flag ที่ไม่ต้องการออกไปได้ (เดิมทีมีแค่ setFlags กับ addFlags)

Intent intent = new Intent(MainActivity.this, OmgActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

// ลบบาง Flag ออก
intent.removeFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

        เพิ่ม Constant สำหรับใช้ใน Intent (Action, Category และ Extra) เข้ามาใหม่

Intent.ACTION_CLEAR_PACKAGE     
Intent.CATEGORY_TYPED_OPENABLE     
Intent.CATEGORY_VR_HOME     
Intent.EXTRA_CONTENT_ANNOTATIONS     
Intent.EXTRA_QUICK_VIEW_ADVANCED

        และประกาศ Deprecated Constant ของ App Shortcut ทั้งหมด แล้วให้เปลี่ยนไปใช้คำสั่งที่เพิ่มเข้ามาใหม่ใน ShortcutManager แทน

// Deprecated
Intent.EXTRA_SHORTCUT_ICON 
Intent.EXTRA_SHORTCUT_ICON_RESOURCE
Intent.EXTRA_SHORTCUT_INTENT 
Intent.EXTRA_SHORTCUT_NAME

// ใช้อันนี้แทน
ShortcutManager manager = (ShortcutManager) getSystemService(SHORTCUT_SERVICE);
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(getContext(), name)
        .setIcon(icon)
        .setIntent(intent)
        .build();
Intent shortcutResultIntent = manager.createShortcutResultIntent(shortcutInfo);


Package Manager

        ชื่อเต็ม : android.content.pm.PackageManager

        เพิ่ม Feature เข้ามาใหม่ 3 ตัว

PackageManager.FEATURE_EMBEDDED     
PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE     
PackageManager.FEATURE_WIFI_AWARE


Bitmap

        คลาส Bitmap มีการเพิ่ม​ Static Method ที่ใช้ในการสร้าง Bitmap Instance มา 2 ตัว สำหรับการสร้าง Bitmap แบบ ARGB_8888 หรือ RGBA_16F

Bitmap createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config, boolean hasAlpha)
Bitmap createBitmap(int width, int height, Bitmap.Config config, boolean hasAlpha)

        ส่วน BitmapFactory.Options เพิ่มตัวแปรที่ชื่อว่า outConfig เข้ามาด้วย เป็นค่าที่บอกว่า Bitmap นั้นๆใช้ Decode แบบไหนอยู่

BitmapFactory.Options options = new BitmapFactory.Options();
options.outConfig = Bitmap.Config.RGBA_F16;


Canvas

        ชื่อเต็ม : android.graphics.Canvas

        มีการประกาศ Deprecated คำสั่งตระกูล Clip ที่ต้องกำหนด Region.Op ทุกตัว เพื่อให้ไปใช้เป็นคำสั่ง Clip Out แทน

// Deprecated
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rectF, Region.Op op)
boolean clipRect(float left, float top, float right, Region.Op op)

// ใช้คำสั่งเหล่านี้แทน 
boolean clipOutPath(Path path)     
boolean clipOutRect(Rect rect)     
boolean clipOutRect(RectF rectF)     
boolean clipOutRect(float left, float top, float right, float bottom)
boolean clipOutRect(int left, int top, int right, int bottom)


Color

        ชื่อเต็ม : android.graphics.Color

        เพิ่มคำสั่งใหม่เข้ามาเยอะมากกกกกกกกกกกกกกก


TypeFace

        ชื่อเต็ม : android.graphics.Typeface

        เพิ่มคำสั่งสร้าง TypeFace โดยใช้ Font Provider (ของเล่นใหม่เช่นกัน) ได้แล้ว

void create(FontRequest request, Typeface.FontRequestCallback callback)


Sensor

        ชื่อเต็ม : android.hardware.Sensor

        เพิ่มประเภทของ Sensor เข้ามาใหม่อีก 2 แบบ

Sensor.TYPE_ACCELEROMETER_UNCALIBRATED     
Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT    


Location

        ชื่อเต็ม : android.location.Location

        เพิ่มคำสั่งสำหรับ Bearing Accuracy, Speed Accuracy และ Vertical Accuracy

float getBearingAccuracyDegrees()     
float getSpeedAccuracyMetersPerSecond()     
float getVerticalAccuracyMeters()     

boolean hasBearingAccuracy()     
boolean hasSpeedAccuracy()     
boolean hasVerticalAccuracy()     

void removeBearingAccuracy()     
void removeSpeedAccuracy()     
void removeVerticalAccuracy()     

void setBearingAccuracyDegrees(float bearingAccuracyDegrees)     
void setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond)     
void setVerticalAccuracyMeters(float verticalAccuracyMeters)


ExifInterface

        ชื่อเต็ม : android.media.ExifInterface

        เพิ่มคำสั่งสำหรับ Thumbnail ใน EXIF นั้นๆ

Bitmap getThumbnailBitmap()     
byte[] getThumbnailBytes()     
boolean isThumbnailCompressed()

        และเพิ่ม Tag ใหม่เพื่อให้ครอบคลุมมากขึ้น

ExifInterface.TAG_DEFAULT_CROP_SIZE     
ExifInterface.TAG_DNG_VERSION     
ExifInterface.TAG_NEW_SUBFILE_TYPE     
ExifInterface.TAG_ORF_ASPECT_FRAME     
ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH     
ExifInterface.TAG_ORF_PREVIEW_IMAGE_START     
ExifInterface.TAG_ORF_THUMBNAIL_IMAGE     
ExifInterface.TAG_RW2_ISO     
ExifInterface.TAG_RW2_JPG_FROM_RAW     
ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER     
ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER     
ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER     
ExifInterface.TAG_RW2_SENSOR_TOP_BORDER     
ExifInterface.TAG_SUBFILE_TYPE


Media Recorder

        มี Output Format แบบ MPEG_2_TS แล้วนะ

MediaRecorder.OutputFormat.MPEG_2_TS


Build 

        ชื่อเต็ม : android.os.Build

        เปลี่ยนวิธีการดึงค่า Serial ที่เดิมจะเรียกจากตัวแปรโดยตรง ให้ไปเรียกผ่าน Getter ที่เพิ่มเข้ามาใหม่แทน

// Deprecated
String serial = Build.SERIAL;

// ใช้คำสั่งนี้แทน
String serial = Build.getSerial();

        และเพิ่ม Version Code สำหรับ Android O เข้ามาด้วย

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // DO something
}


Bundle

        ชื่อเต็ม : android.os.Bundle

        เพิ่มคำสั่งสำหรับ Hard Copy เข้ามาเพื่อให้สามารถสร้าง Bundle จาก Bundle อีกตัวได้ โดยที่ทั้งสองตัวนั้นเป็น Object คนละตัวกัน

Bundle oldBundle = new Bundle();
oldBundle.putString(KEY_NAME, name);
oldBundle.putString(KEY_COUNTRY, country);

// สร้าง Bundle ตัวใหม่ โดยที่ยังมีค่าเดิมที่กำหนดไว้อยู่
Bundle newBundle = oldBundle.deepcopy();


Parcelable

        Parcel รองรับ SparseIntArray แล้ว จากเดิมที่รองรับแค่ SparseArray กับ SparseBooleanArray (ทำไมไม่มี SparseLongArray ด้วยนะ)


PreferenceDataStore

        เป็นคลาสใหม่ที่สร้างขึ้นมาใช้ใน Preference เวลาที่ผู้ใช้ตั้งค่าการทำงานต่างๆ (หน้าที่ใช้ PreferenceActivity หรือ PreferenceFragment) ซึ่งเป็นคลาสที่สามารถนำมาใช้แทนที่ SharedPreference เดิมได้เลย

public class UserSettingFragment extends PreferenceFragment {

    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        ...
        
        PreferenceManager.setDefaultValues(getActivity(), R.xml.advanced_preferences, false);
        addPreferencesFromResource(R.xml.activity_preference);
    }

    ...

    public void updateNamePreference() {
        PreferenceDataStore dataStore = getPreferenceManager().getPreferenceDataStore();
        dataStore.putString(KEY_NAME, etUsername.getText().toString());
    }
}

        จากการนั่งดูแล้วก็ยังไม่เข้าใจเหมือนกันว่าทำไมถึงสร้างคลาสตัวนี้ขึ้นมาเพื่อใช้แทน SharedPreference เฉพาะใน Preference เท่านั้น...

        แต่ถ้าเป็น Activity หรือ Fragment ทั่วๆไป ไม่ต้องสนใจครับ ใช้ SharedPreference เหมือนเดิมน่ะแหละ


Settings

        ชื่อเต็ม : android.provider.Settings

        เพิ่ม Action และ Extra ที่เกี่ยวกับ Notification เพิ่มเข้ามา

Settings.ACTION_APP_NOTIFICATION_SETTINGS     
Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS     
Settings.ACTION_MANAGE_EXTERNAL_SOURCES     
Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS     
Settings.EXTRA_APP_PACKAGE     
Settings.EXTRA_CHANNEL_ID


TelephonyManager

        ชื่อเต็ม : android.telephony.TelephonyManager

        มีหลายๆอย่างในนี้ที่เพิ่มเข้ามา แต่บางอย่างก็ไม่น่าสนใจ บางอย่างก็มีไว้สำหรับ System App ซึ่งที่น่าสนใจที่สุดก็คงจะเป็นการที่เปิดให้สามารถส่ง USSD และรับ Reponse ได้แล้ว เพราะเดิมต้องไปเรียกผ่าน Intent โดยใช้ ACTION_CALL ซึ่งไม่สามารถดักข้อมูลที่ส่งกลับมาได้

String ussdCode = "*103#";

TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
manager.sendUssdRequest(ussdCode, new TelephonyManager.OnReceiveUssdResponseCallback() {
    @Override
    public void onReceiveUssdResponse(String request, CharSequence response) {
        super.onReceiveUssdResponse(request, response);

    }

    @Override
    public void onReceiveUssdResponseFailed(String request, int failureCode) {
        super.onReceiveUssdResponseFailed(request, failureCode);

    }
}, new Handler());


TransitionListenerAdapter

        ชื่อเต็ม : android.transition.TransitionListenerAdapter

        เพิ่มเข้ามาเพื่อทดแทนการใช้ Transition.TransitionListener ที่ทำให้โค้ดยาวเหยียด (เหมือนกับ AnimatorListenerAdapter เลย)


InputDevice

        ชื่อเต็ม : android.view.InputDevice

        เพิ่ม Constant เข้ามาใหม่อีก 2 ตัว

InputDevice.SOURCE_MOUSE_RELATIVE     
InputDevice.SOURCE_ROTARY_ENCODER

        มันมีใครควบคุมอุปกรณ์แอนดรอยด์ผ่าน Input Device พวกนี้ด้วยหรอเนี่ย...


MotionEvent

        ชื่อเต็ม : android.view.MotionEvent

        เพิ่ม Constant ที่ชื่อว่า AXIS_SCROLL เพื่อรองรับกับ Input Device ที่สามารถ Scroll ได้

MotionEvent.AXIS_SCROLL     


ViewGroup

        ชื่อเต็ม : android.view.ViewGroup

        ประกาศ Deprecated บางคำส่ง

// Deprecated 
void invalidateChild(View child, Rect dirty)
ViewParent invalidateChildInParent(int[] location, Rect dirty)

// ใช้คำสั่งนี้แทน
onDescendantInvalidated(View child, View target)


DatePicker

        ชื่อเต็ม : android.widget.DatePicker

        เพิ่ม Listener เมื่อมีการเปลี่ยนวันที่

DatePicker datePicker = (DatePicker) findViewById(R.id.date_picker);
datePicker.setOnDateChangedListener(new DatePicker.OnDateChangedListener() {
    @Override
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

    }
});


ProgressBar

        ชื่อเต็ม : android.widget.ProgressBar

        สามารถกำหนด Minimum Progress ได้แล้วววววววววววววว

ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);

// กำหนดค่า Minimum Progress
int minimumProgress = 10;
progressBar.setMin(minimumProgress);

// ดังค่า Minimum Progress
int minProgress = progressBar.getMin();

        และเพิ่มคำสั่ง isAnimating เพื่อใช้แทน isIndeterminate ด้วย เพราะว่า isAnimating นั้นหมายถึง Progress Bar กำลังอยู่ใน Indeterminate Mode และ Visible อยู่ (นอกเหนือจากนี้จะเป็น False ทั้งหมด)

boolean isAnimating = progressBar.isAnimating();


เก็บตกคำสั่งอื่นๆที่ถูก Remove และ Deprecate

        • คลาส ProgressDialog ถูกประกาศ Deprecate ทั้งคลาส ให้สร้าง Dialog ที่มี Progress Bar เอง
        • คลาส DialerFilter ถูกประกาศ Deprecate ทั้งคลาส ให้ใช้ Custom View หรือ Layout จัดการเองแทน
        • คลาส ZoomButton และ ZoomButtonsController ถูกประกาศ Deprecate ให้ใช้วิธีเขียนคำสั่งเพื่อควบคุมการทำงานเอง
        • คำสั่ง findViewTraversal และ findViewWithTagTraversal ในคลาส ListView ถูกลบออกไป
        • คลาส RasterizerSpan ถูกลบออกไป
        • คลาส LayerRasterizer ถูกลบออกไป
        • คลาส Rasterizer ถูกลบออกไป
        • คลาส PskKeyManager ถูกลบออกไป

สรุป

        เยอะมากกกกกกกกก นี่ขนาดตัดหลายๆอันออกไปแล้วนะ (แถมเอามาแค่ Android เท่านั้น ยังไม่นับส่วนของ Java) ซึ่งหลายๆคำสั่งที่เปลี่ยนไปก็น่าจะกระทบกับนักพัฒนาหลายๆคนอยู่บ้าง (เจ้าของบล็อกก็ด้วย) ถ้าจะแก้ไขให้รองรับกับ Android O ก็อย่าลืมเรื่อง Backward Compatibility ด้วยนะ

        สุดท้ายแล้วก็ให้หมั่นอัปเดตอยู่บ่อยๆ ว่าในแต่ละเวอร์ชันเนี่ยมีอะไรเพิ่มเข้ามาบ้าง เปลี่ยนอะไรบ้าง อันไหนเลิกใช้ ให้ใช้อันไหนแทน เพื่อให้แอปฯของผู้ที่หลงเข้ามาอ่านทำงานได้ปกติสุขบนอุปกรณ์แอนดรอยด์ที่ต่างเวอร์ชันกันครับ

แหล่งข้อมูลอ้างอิง ​

        • Android API Differences Report [Android Developers]




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium