มาสำรวจกันว่ามีคำสั่งสำคัญอะไรบ้างที่เปลี่ยนแปลงไปบน Android 8.0 Oreo
หลังจากที่ Android 8.0 Oreo เปิดตัวมาได้ซักพัก ก็ได้มีบทความเกี่ยวกับการเปลี่ยนแปลงในเวอร์ชันใหม่นี้ไปแล้ว ซึ่งเจ้าของบล็อกก็ไม่มีเหตุผลว่าทำไมต้องไปเขียนซ้ำกับคนอื่นอีก ก็เลยหนีมาเขียนเรื่องนี้แทน เพื่อให้ผู้ที่หลงเข้ามาอ่านได้รู้ว่านอกจากฟีเจอร์หลักๆแล้ว มีคำสั่งอะไรบ้างที่เปลี่ยนแปลงไป
ก่อนที่จะอ่านต่อ
ถ้ายังไม่รู้ว่าฟีเจอร์หลักของ Android 8.0 Oreo มีอะไรบ้างที่มีผลต่อนักพัฒนา ให้ไปอ่านจากบทความเพื่อนบ้านก่อนเลย
- มาดูกันว่า Android O มีอะไรใหม่บ้าง ในแบบฉบับนักพัฒนา
- สรุปของเล่นใหม่คร่าวๆใน ANDROID O(MG) ฉบับ DEVELOPER
ป่ะ ลุยกันต่อ
นอกจากจะมีฟีเจอร์ใหม่ๆ และคำสั่งใหม่ๆสำหรับฟีเจอร์เหล่านั้นแล้ว ก็ยังมีการเปลี่ยนแปลงของโค้ดใน API เก่าที่มีอยู่ด้วย ซึ่งเจ้าของบล็อกเข้าไปนั่งไล่ส่องมาแล้ว ก็เลยขอหยิบเฉพาะคำสั่งของคลาสที่สำคัญๆมาให้ดูนะ
Configuration
ชื่อเต็ม : android.content.res.Configuration
มีการเพิ่มคำสั่งเข้ามาเพื่อให้รองรับการแสดงผลบนหน้าจอที่รองรับ HDR และ Wide Color Gamut
val context: Context = ...
val isScreenHdr: Boolean = context.resources.configuration.isScreenHdr
val isScreenWideColorGamut: Boolean = context.resources.configuration.isScreenWideColorGamut
ซึ่งถ้าลงละเอียดไปลึกกว่านั้น ผู้ที่หลงเข้ามาอ่านสามารถดึงค่า Color Mode ด้วยคำสั่งแบบนี้ได้เลย
val colorMode = resources.configuration.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 อยู่หรือไม่
Configuration.UI_MODE_TYPE_VR_HEADSET
Display
ชื่อเต็ม : android.view.Display
เพิ่มคำสั่งสำหรับเช็คว่าหน้าจอที่แสดงผลอยู่นั้นเป็น HDR หรือ Wide Color Gamut หรือป่าว
val windowManager: WindowManager= this
val isHdr = windowManager.defaultDisplay.isHdr
val isWideColorGamut = windowManager.defaultDisplay.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 8.0 Oreo ได้เลย แต่ก็ขึ้นอยู่กับแต่ละเครื่องด้วยว่า 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 วินาที
val windowManager: WindowManager= this
val isHdr = windowManager.defaultDisplay.isHdr
val isWideColorGamut = windowManager.defaultDisplay.isWideColorGamut
ระหว่างที่ Animator กำลังเล่นอยู่นั้น มีคำสั่งสำหรับเช็คได้ว่าตอนนี้กำลังเล่นอยู่ที่วินาทีเท่าไร และกำหนดได้ว่าจะให้ข้ามไปแสดงผลเป็นวินาทีที่เท่าไร (เหมือนเวลาดูหนังแล้วสั่งให้ข้ามไปเล่นวินาทีที่ต้องการ)
val currentPlayTime = animator.currentPlayTime
animator.currentPlayTime = 500
จากคำสั่ง setCurrentPlayTime จะทำให้ Animator ข้ามไปแสดงผลที่ 500 มิลลิวินาทีหรือ 0.5 วินาที ทันที
และสามารถสั่งให้เล่นย้อนกลับได้ด้วยนะเออ
animator.reverse()
ซึ่งคำสั่งสำหรับ Current Play Time และ Reverese นั้นจะใช้ได้ก็ต่อเมื่อ Animator ทำงานแล้วเท่านั้นนะจ๊ะ
เมื่อ Animator สามารถทำงานแบบ Reverse ได้ ดังนั้น Animator Listener จึงเพิ่ม Event เข้ามาอีก 2 ตัวเพื่อรองรับการทำงานแบบ Reverse
animator.addListener(object: Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
}
...
})
แนะนำให้ใช้ AnimatorListenerAdapter แทน Animator.AnimatorListener
Fragment
สำหรับ Fragment Manager ในตอนนี้มี Fragment Lifecycle Callback ให้ใช้แล้วจ้าาาาาาาาาาาาาาา (Support v4 ก็มีให้ใช้แล้วเหมือนกัน)
private val callback: FragmentManager.FragmentLifecycleCallbacks =
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentPreAttached(manager: FragmentManager, fragment: Fragment, context: Context) {}
override fun onFragmentAttached(manager: FragmentManager, fragment: Fragment, context: Context) {}
override fun onFragmentCreated(manager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) {}
override fun onFragmentActivityCreated(manager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) {}
override fun onFragmentViewCreated(manager: FragmentManager, fragment: Fragment, view: View, savedInstanceState: Bundle?) {}
override fun onFragmentStarted(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentResumed(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentPaused(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentStopped(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentSaveInstanceState(manager: FragmentManager, fragment: Fragment, outState: Bundle?) {}
override fun onFragmentViewDestroyed(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentDestroyed(manager: FragmentManager, fragment: Fragment) {}
override fun onFragmentDetached(manager: FragmentManager, fragment: Fragment) {}
}
ซึ่ง Callback ตัวนี้จะบอกให้หมดว่า Fragment ตัวไหนเกิด Lifecycle อะไร ส่วนการใช้งานก็จะเป็นลักษณะของ Register/Unregister
val manager = supportFragmentManager
manager.registerFragmentLifecycleCallbacks(callback, true)
// เมื่อไม่ใช้งานแล้ว
manager.unregisterFragmentLifecycleCallbacks(callback)
คำสั่ง Register นั้นสามารถกำหนดได้ว่าจะให้คำสั่งนี้ Recursive ใน Child Fragment Manager ด้วยหรือไม่
ส่วน Fragment นั้นก็มีการเพิ่มคำสั่งเข้ามาเล็กน้อย
fun isStateSaved(): Boolean
fun postponeEnterTransition()
fun startPostponedEnterTransition()
isStateSaved
เช็คว่า Fragment State มีการ Save เก็บไว้เรียบร้อยแล้วหรือยัง
postponeEnterTransition
กำหนดให้ Transition ของ Fragment ตัวนั้นอย่าเพิ่งทำงาน จนกว่าจะสั่งด้วยคำสั่ง startPostponedEnterTransition
(มี Transition แต่ยังไม่อยากให้เล่นทันที)
startPostponedEnterTransition
สั่งให้ Transition ของ Fragment ที่กำหนดด้วยคำสั่ง postponeEnterTransition เริ่มทำงาน
Notification
เพิ่มคำสั่งเพื่อรองรับ Channel และ Channel Group ซึ่งเจ้าของบล็อกแยกเป็นบทความเรื่องนี้ให้แล้ว สามารถตามไปอ่านกันได้ที่ Notification in Android ตอนที่ 5 — Notification Channel
Activity
สำหรับคลาส Activity จะเพิ่มคำสั่งต่างๆเพื่อให้รองรับการทำงานแบบ Picture in Picture และ Freeform Window
fun enterPictureInPictureMode(arge: PictureInPictureArgs): Boolean
fun isOverlayWithDecorCaptionEnabled(): Boolean
fun setOverlayWithDecorCaptionEnabled(enabled: Boolean)
fun setPictureInPictureArgs(args: PictureInPictureArgs)
และมีการเพิ่มคำสั่ง isActivityTransitionRunning เพื่อให้เช็คได้ว่า Activity ณ ตอนนั้นกำลังแสดง Transition ใดๆอยู่หรือป่าว
isActivityTransitionRunning(): Boolean
AppWidgetManager
ชื่อเต็ม : android.appwidget.AppWidgetManager
เพิ่มคำสั่งสำหรับแปะ Widget ลงใน Home Screen ผ่านโค้ดได้แล้ว เพราะเดิมทีนั้นให้ผู้ใช้เป็นคนลาก Widget ไปใส่เองเท่านั้น
fun isRequestPinAppWidgetSupported(): Boolean
fun requestPinAppWidget(ComponentName provider, PendingIntent successCallback): Boolean
Content Provider
มีการ Overload Method เพิ่มเข้ามาให้กับ Query และ Refresh เพื่อให้รองรับการยกเลิกกลางคันโดยใช้คลาส CancellationSignal
val signal = CancellationSignal()
// ใช้กับ Query
provider.query(uri, projection, queryArgs, signal)
// ใช้กับ Refresh
provider.refresh(uri, args, signal)
// เมื่อต้องการยกเลิกการ Query หรือ Refresh กลางคัน
signal.cancel()
Context
มีการเพิ่ม Service Manager เข้ามาใหม่ทั้งหมด 4 ตัวด้วยกัน (บางอันใช้สำหรับ System App เท่านั้น)
Context.COMPANION_DEVICE_SERVICE
Context.STORAGE_STATS_SERVICE
Context.TEXT_CLASSIFICATION_SERVICE
Context.WIFI_AWARE_SERVICE
และเพิ่มคำสั่งเข้ามาอีก 1 ตัว
fun createContextForSplit(splitName: String): Context
เป็นคำสั่งที่ใช้สำหรับสร้าง Context ขึ้นมาใหม่โดยใช้ Context ที่มีอยู่ ซึ่ง Context ที่ถูกสร้างขึ้นมาใหม่จะเป็นคนละตัวกับ Context ตัวเก่า แต่ก็ได้ Resource จากตัวเก่ามาใช้งานได้อยู่นะ
Intent
เพิ่มคำสั่ง removeFlags มาให้ จะได้ลบ Flag ที่ไม่ต้องการออกไปได้ (เดิมทีมีแค่ setFlags กับ addFlags)
val intent = Intent(...)
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
// ใช้อันนี้แทน
val manager: ShortcutManager = getSystemService(Context.SHORTCUT_SERVICE) as ShortcutManager
val info = ShortcutInfo.Builder(context, name)
.setIcon(icon)
.setIntent(intent)
.build()
val intent = manager.createShortcutResultIntent(info)
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
fun createBitmap(display: DisplayMetrics, width: Int, height: Int, config: Bitmap.Config, hasAlpha: Boolean): Bitmap
fun createBitmap(width: Int, height: Int, config: Bitmap.Config, hasAlpha: Boolean): Bitmap
ส่วน 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
fun clipPath(path: Path, op: Region.Op): Boolean
fun clipRect(rect: Rec, op: Region.Op): Boolean
fun clipRect(rectF: RectF, op: Region.Op): Boolean
fun clipRect(left: Float, top: Float, right: Float, op: Region.Op): Boolean
// ใช้คำสั่งเหล่านี้แทน
fun clipOutPath(path: Path): Boolean
fun clipOutRect(rect: Rect): Boolean
fun clipOutRect(rectF: RectF): Boolean
fun clipOutRect(left: Float, top: Float, right: Float, bottom: Float): Boolean
fun clipOutRect(left: Int, top: Int, right: Int, bottom: Int): Boolean
Color
ชื่อเต็ม : android.graphics.Color
เพิ่มคำสั่งใหม่เข้ามาเยอะมากกกกกกกกกกกกกกก
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
fun getBearingAccuracyDegrees(): Float
fun getSpeedAccuracyMetersPerSecond(): Float
fun getVerticalAccuracyMeters(): Float
fun hasBearingAccuracy(): Boolean
fun hasSpeedAccuracy(): Boolean
fun hasVerticalAccuracy(): Boolean
fun removeBearingAccuracy()
fun removeSpeedAccuracy()
fun removeVerticalAccuracy()
fun setBearingAccuracyDegrees(float bearingAccuracyDegrees)
fun setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond)
fun setVerticalAccuracyMeters(float verticalAccuracyMeters)
ExifInterface
ชื่อเต็ม : android.media.ExifInterface
เพิ่มคำสั่งสำหรับ Thumbnail ใน EXIF นั้นๆ
fun getThumbnailBitmap(): Bitmap
fun getThumbnailBytes(): ByteArray
fun isThumbnailCompressed(): Boolean
และเพิ่ม 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
var serial = Build.SERIAL
// ใช้คำสั่งนี้แทน
var serial = Build.getSerial()
และเพิ่ม Version Code สำหรับ Android 8.0 Oreo เข้ามาด้วย
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// DO something
}
Bundle
ชื่อเต็ม : android.os.Bundle
เพิ่มคำสั่งสำหรับ Hard Copy เข้ามาเพื่อให้สามารถสร้าง Bundle จาก Bundle อีกตัวได้ โดยที่ทั้งสองตัวนั้นเป็น Object คนละตัวกัน
val oldBundle = Bundle()
oldBundle.putString(KEY_NAME, name)
oldBundle.putString(KEY_COUNTRY, country)
// สร้าง Bundle ตัวใหม่ โดยที่ยังมีค่าเดิมที่กำหนดไว้อยู่
val newBundle: Bundle = oldBundle.deepCopy()
Parcelable
Parcel รองรับ SparseIntArray แล้ว จากเดิมที่รองรับแค่ SparseArray กับ SparseBooleanArray (ทำไมไม่มี SparseLongArray ด้วยนะ)
PreferenceDataStore
เป็นคลาสใหม่ที่สร้างขึ้นมาใช้ใน Preference เวลาที่ผู้ใช้ตั้งค่าการทำงานต่างๆ (หน้าที่ใช้ PreferenceActivity หรือ PreferenceFragment) ซึ่งเป็นคลาสที่สามารถนำมาใช้แทนที่ SharedPreference เดิมได้เลย
class UserSettingFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
...
PreferenceManager.setDefaultValues(activity, R.xml.advanced_preferences, false)
addPreferencesFromResource(R.xml.activity_preference)
}
fun updateNamePreference() {
val dataStore = preferenceManager.preferenceDataStore
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
ซึ่งไม่สามารถดักข้อมูลที่ส่งกลับมาได้
val ussdCode = "*103#"
val manager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
manager.sendUssdRequest(ussdCode, object : TelephonyManager.UssdResponseCallback() {
fun onReceiveUssdResponse(request: String?, response: CharSequence?) {
}
fun onReceiveUssdResponseFailed(request: String?, failureCode: Int) {
}
}, 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
fun invalidateChild(child: View, dirty: Rect)
fun invalidateChildInParent(location: IntArray, dirty: Rect): ViewParent
// ใช้คำสั่งนี้แทน
fun onDescendantInvalidated(child: View, target: View)
DatePicker
ชื่อเต็ม : android.widget.DatePicker
เพิ่ม Listener เมื่อมีการเปลี่ยนวันที่
val datePicker: DatePicker = ...
datePicker.setOnDateChangedListener { view, year, monthOfYear, dayOfMonth ->
// Do something when date changed
}
ProgressBar
ชื่อเต็ม : android.widget.ProgressBar
สามารถกำหนด Minimum Progress ได้แล้วววววววววววววว
val progressBar: ProgressBar = ...
// กำหนดค่า Minimum Progress
val minimumProgress = 10
progressBar.min = minimumProgress
// ดังค่า Minimum Progress
val minProgress = progressBar.min
และเพิ่มคำสั่ง isAnimating
เพื่อใช้แทน isIndeterminate
ด้วย เพราะว่า isAnimating
นั้นหมายถึง ProgressBar กำลังอยู่ใน Indeterminate Mode และ Visible อยู่ (นอกเหนือจากนี้จะเป็น False ทั้งหมด)
val isAnimating: Boolean = progressBar.isAnimating()
เก็บตกคำสั่งอื่นๆที่ถูก Remove และ Deprecate
- คลาส
ProgressDialog
ถูกประกาศ Deprecate ทั้งคลาส ให้สร้างDialog
ที่มี Progress Bar เอง - คลาส
DialerFilter
ถูกประกาศ Deprecate ทั้งคลาส ให้ใช้ Custom View หรือ Layout จัดการเองแทน - คลาส
ZoomButton
และZoomButtonsController
ถูกประกาศ Deprecate ให้ใช้วิธีเขียนคำสั่งเพื่อควบคุมการทำงานเอง - คำสั่ง
findViewTraversal
และfindViewWithTagTraversal
ในคลาส K ถูกลบออกไป - คลาส
RasterizerSpan
ถูกลบออกไป - คลาส
LayerRasterizer
ถูกลบออกไป - คลาส
Rasterizer
ถูกลบออกไป - คลาส
PskKeyManager
ถูกลบออกไป
สรุป
เยอะมากกกกกกกกก นี่ขนาดตัดหลายๆอันออกไปแล้วนะ (แถมเอามาแค่ Android เท่านั้น ยังไม่นับส่วนของ Java) ซึ่งหลายๆคำสั่งที่เปลี่ยนไปก็น่าจะกระทบกับนักพัฒนาหลายๆคนอยู่บ้าง (เจ้าของบล็อกก็ด้วย) ถ้าจะแก้ไขให้รองรับกับ Android 8.0 Oreo ก็อย่าลืมเรื่อง Backward Compatibility ด้วยนะ
สุดท้ายแล้วก็ให้หมั่นอัปเดตอยู่บ่อยๆ ว่าในแต่ละเวอร์ชันเนี่ยมีอะไรเพิ่มเข้ามาบ้าง เปลี่ยนอะไรบ้าง อันไหนเลิกใช้ ให้ใช้อันไหนแทน เพื่อให้แอปฯของผู้ที่หลงเข้ามาอ่านทำงานได้ปกติสุขบนอุปกรณ์แอนดรอยด์ที่ต่างเวอร์ชันกันครับ