เป็นเรื่องธรรมดาสำหรับนักพัฒนาแอนดรอยด์ที่เขียนโค้ดในบางครั้งแล้วพบว่าการทำงานบางอย่างของแอนดรอยด์จะต้องเขียนโค้ดแยกกันตามเวอร์ชันด้วย
เจ้าของบล็อกต้องการเขียนโค้ดเพื่อเช็คขนาดของหน้าจอ แล้วพบว่าคำสั่งสำหรับเช็คขนาดหน้าจอมี 2 แบบ
// Android 4.2 Jelly Bean (API 17) or higher
val windowManager = ...
val display = windowManager.defaultDisplay
val metrics = DisplayMetrics()
display.getRealMetrics(metrics)
Pair(metrics.widthPixels, metrics.heightPixels)
// Lower than Android 4.2 Jelly Bean (API 17)
val windowManager = ...
try {
val rawHeight: Method = Display::class.java.getMethod("getRawHeight")
val rawWidth: Method = Display::class.java.getMethod("getRawWidth")
Pair(rawWidth.invoke(display) as Int, rawHeight.invoke(display) as Int)
} catch (e: Exception) {
val display = windowManager.defaultDisplay
Pair(display.width, display.height)
}
จะเห็นว่าคำสั่งระหว่าง 2 เวอร์ชันนั้นมีโค้ดที่แตกต่างกันโดยสิ้นเชิง นั่นก็เพราะว่าใน API 17 ได้มีการเพิ่มคำสั่งใหม่ๆเข้ามา รวมไปถึงการเช็คขนาดหน้าจอด้วยเช่นกัน
และนักพัฒนาก็จะต้อง
ซึ่งโค้ดในลักษณะนี้สามารถพบเจอได้บ่อยมาก เพราะมีคำสั่งอีกมากมายที่ถูกเปลี่ยนแปลงในแอนดรอยด์แต่ละเวอร์ชัน
นอกจากจะบอกคำสั่งที่มีในแต่ละคลาสของแอนดรอยด์แล้ว ยังมีบอกอีกด้วยว่าคำสั่งแต่ละตัวนั้นถูกเพิ่มเข้ามาในแอนดรอยด์เวอร์ชันอะไร หรือยกเลิกใช้งานเมื่อไร รวมไปถึงแนะนำ Best Practice สำหรับการใช้งานคำสั่งนั้นๆอีกด้วย
เมื่อเจอกับกรณีที่ต้องเรียกใช้คำสั่งที่มีแค่ใน API เวอร์ชันใหม่ๆ ก็ควรใส่ Annotion ที่ชื่อ @RequireApi ไว้ที่คำสั่งนั้นๆด้วย เพื่อให้ Android Studio (และนักพัฒนา) รู้ว่า Method ดังกล่าวมีการเรียกใช้คำสั่งของ API เวอร์ชันใหม่ๆ
เมื่อมีการเรียกใช้งานคำสั่งนี้ที่ไหนก็ตาม Android Studio จะทำการเช็คทันทีว่าโค้ดในจุดนั้นมีโอกาสที่จะทำงานบนแอนดรอยด์เวอร์ชันที่ต่ำกว่านั้นหรือไม่ แล้วจะแจ้งเตือนให้นักพัฒนารู้ว่าต้องเขียนโค้ดเพื่อป้องกันกรณีนี้ด้วย
และนักพัฒนาก็จะต้อง
แยกโค้ดตามเวอร์ชันนั่นเอง
ด้วยการใช้ If-else เพื่อเช็คว่าแอปกำลังทำงานอยู่บนอุปกรณ์แอนดรอยด์เวอร์ชันอะไร เพื่อให้คำสั่งทำงานตรงกับเวอร์ชันนั้นๆfun getScreenResolution(windowManager: WindowManager): Pair<Int, Int>? {
val display = windowManager.defaultDisplay
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val metrics = DisplayMetrics()
display.getRealMetrics(metrics)
Pair(metrics.widthPixels, metrics.heightPixels)
} else {
try {
val rawHeight: Method = Display::class.java.getMethod("getRawHeight")
val rawWidth: Method = Display::class.java.getMethod("getRawWidth")
Pair(rawWidth.invoke(display) as Int, rawHeight.invoke(display) as Int)
} catch (e: Exception) {
Pair(display.width, display.height)
}
}
}
ซึ่งโค้ดในลักษณะนี้สามารถพบเจอได้บ่อยมาก เพราะมีคำสั่งอีกมากมายที่ถูกเปลี่ยนแปลงในแอนดรอยด์แต่ละเวอร์ชัน
API Reference จึงเป็นสิ่งสำคัญสำหรับนักพัฒนา
การศึกษาคำสั่งต่างๆใน API Reference ในเว็ป Android Developers จึงเป็นสิ่งสำคัญมากๆสำหรับนักพัฒนาแอนดรอยด์นอกจากจะบอกคำสั่งที่มีในแต่ละคลาสของแอนดรอยด์แล้ว ยังมีบอกอีกด้วยว่าคำสั่งแต่ละตัวนั้นถูกเพิ่มเข้ามาในแอนดรอยด์เวอร์ชันอะไร หรือยกเลิกใช้งานเมื่อไร รวมไปถึงแนะนำ Best Practice สำหรับการใช้งานคำสั่งนั้นๆอีกด้วย
แต่ไม่ใช่ทุกคำสั่งที่จะทำงานได้ในทุกเวอร์ชัน
ก็ต้องมีบ้างที่ฟีเจอร์ใหม่ๆมาพร้อมกับแอนดรอยด์เวอร์ชันใหม่ๆ ดังนั้นเวอร์ชันเก่าๆจึงไม่ต้องพูดถึงเมื่อเจอกับกรณีที่ต้องเรียกใช้คำสั่งที่มีแค่ใน API เวอร์ชันใหม่ๆ ก็ควรใส่ Annotion ที่ชื่อ @RequireApi ไว้ที่คำสั่งนั้นๆด้วย เพื่อให้ Android Studio (และนักพัฒนา) รู้ว่า Method ดังกล่าวมีการเรียกใช้คำสั่งของ API เวอร์ชันใหม่ๆ
@RequiresApi(Build.VERSION_CODES.O)
fun initNotificationChannel(context: Context) {
...
}
ซึ่งจริงๆแล้ว Android API ที่ใช้งานกันอยู่ทุกวันนี้ก็มีการใส่ @RequireApi ไว้เพื่อให้นักพัฒนารู้ว่าคำสั่งนั้นๆใช้ได้เฉพาะบนแอนดรอยด์เวอร์ชันไหน
ทั้งนี้การเขียนโค้ดเพื่อรับมือกับเวอร์ชันแอนดรอยด์ที่มีหลากหลายมากเกินไป ควรดูว่าคำสั่งดังกล่าวจำเป็นต้องเขียนโค้ดแยกเวอร์ชันหรือไม่ ถ้าทำได้ก็ดี แต่ถ้าทำไม่ได้ก็ควรเขียนโค้ดเผื่อสำหรับกรณีที่ใช้งานฟีเจอร์นั้นๆไม่ได้ด้วยนะ