CPU Architecture และ Android ABI ที่นักพัฒนาแอปควรรู้
ด้วยความที่ Android OS เป็น Open Source Operating System จึงทำให้รองรับอุปกรณ์ได้หลากหลาย ไม่ว่าจะเป็น Form Factor ต่าง ๆ ไปจนถึงฮาร์ดแวร์ที่มีความแตกต่างกันในแต่ละอุปกรณ์ และรวมไปถึง CPU ด้วยเช่นกัน
นักพัฒนาอาจจะคุ้นเคยกันดีกับ ARM ที่เป็น CPU Architecture ยอดนิยมสำหรับอุปกรณ์พกพาอย่าง Smartphone หรือ Tablet โดย CPU Architecture แต่ละแบบ (รวมไปถึง ARM) ก็จะรองรับชุดคำสั่ง (Instruction Set) แตกต่างกันไปตามการออกแบบของผู้คิดค้น
เพื่อให้รองรับกับ CPU Architecture ในแต่ละแบบจึงทำให้ Android OS มีสิ่งที่เรียกว่า Application Binary Interface หรือ ABI เพื่อเป็นตัวกลางในการทำงานและสื่อสารกับ CPU ที่มี CPU Architecture แตกต่างกันออกไป และด้วยการทำงานของ ABI จึงทำให้ Android App สามารถทำงานได้โดยไม่ต้องสนใจว่าจะเป็น CPU Architecture แบบไหนนั่นเอง
โดยในปัจจุบัน ABI จะมีอยู่ทั้งหมด 4 แบบด้วยกัน
Android ABI | CPU Architecture | Bits | Initially developed by |
---|---|---|---|
armeabi-v7a | ARM | 32-bit | Arm |
arm64-v8a | ARM | 64-bit | Arm |
x86 | x86 | 32-bit | Intel |
x86_64 | x86 | 64-bit | Intel |
เมื่อก่อนเคยรองรับ ARMv5 และ MIPS ด้วย แต่เนื่องจากไม่ได้รับความนิยม (ตามยุคสมัย) จึงถูกยกเลิกไป
Android ABI ส่งผลต่อการพัฒนาแอปอย่างไรบ้าง?
แอปทั่วไปที่เรียกใช้คำสั่งของ Android Platform API จะทำงานได้ปกติไม่ว่าจะเปิดใช้งานอยู่บนอุปกรณ์แอนดรอยด์ที่ใช้ CPU หรือ ABI แบบใดก็ตาม
แต่สำหรับการทำงานบางอย่างในระดับ Low Level ที่จำเป็นต้องใช้ Native Development Kit หรือ Android NDK ในการพัฒนาแอป (OpenCV หรือ FFmpeg เป็นต้น) จะต้องคำนึงถึงชุดคำสั่งที่ใช้ด้วย เพราะไม่ใช่ทุกชุดคำสั่งที่จะรองรับได้ทุก ABI
นั่นจึงทำให้แอปที่มีการใช้ Android NDK หรือ Native Library ที่เป็นไฟล์ .so
อาจจะรองรับแค่บาง ABI เท่านั้น ซึ่งจะขึ้นอยู่กับนักพัฒนาว่าจะทำให้รองรับกับ ABI ใดบ้าง
โดยไฟล์ .so
จะอยู่ใน lib
ของไฟล์ APK และมีการแยกตาม ABI
app.apk
├── lib
│ ├── arm64-v8a
│ │ └── libavcodec.so
│ ├── armeabi-v7a
│ │ └── libavcodec.so
│ ├── x86
│ │ └── libavcodec.so
│ └── x86_64
│ └── libavcodec.so
└── ...
ดังนั้นถ้าข้างในไฟล์ APK ของแอปตัวไหนขาดไฟล์ .so
ของ ABI บางตัวไป ก็แปลว่าแอปนั้นก็อาจจะไม่รองรับการใช้งานกับอุปกรณ์แอนดรอยด์ที่ใช้ ABI ตัวนั้นก็เป็นได้
และนอกจากนี้นักพัฒนาสามารถกำหนดใน Gradle ได้ด้วยว่าต้องการให้แอปรองรับ ABI ใดบ้าง ซึ่งจะทำให้ไฟล์ APK หรือ Android App Bundle ถูกสร้างขึ้นมาโดยมีไฟล์ .so
เฉพาะ ABI ที่กำหนดไว้ใน Gradle เท่านั้น
// build.gradle.kts
android {
/* ... */
defaultConfig {
ndk {
abiFilters("arm64-v8a", "x86_64")
}
}
}
ซึ่งจะเหมาะกับกรณีที่แอปต้องการรองรับแค่บาง ABI ด้วยเหตุผลใดก็ตาม เช่น Native Library ที่ใช้ในแอปรองรับแค่ CPU ที่เป็น ARM เท่านั้น
และการที่ Android App Bundle สามารถลดขนาดไฟล์ APK ที่ติดตั้งลงในอุปกรณ์แอนดรอยด์ได้มากกว่าไฟล์ APK ที่สร้างจาก Android Studio ตามปกติ ส่วนหนึ่งก็เพราะไฟล์ APK ที่ได้จาก Android App Bundle จะมีแค่ไฟล์ .so
ที่อิงตาม ABI ของอุปกรณ์แอนดรอยด์แต่ละเครื่องให้โดยเฉพาะ
CPU แบบ 32-bit และ 64-bit
อย่างที่เรารู้กันว่า CPU แบบ 64-bit จะรองรับการประมวลผลได้ดีกว่า 32-bit และเพื่อให้ระบบแอนดรอยด์รองรับแอปที่มีขนาดใหญ่มากขึ้น และการทำงานที่ซับซ้อนมากขึ้นในอนาคต จึงทำให้ Android OS นั้น...
- รองรับ CPU แบบ 64-bit มาตั้งแต่ Android 5.0 Lollipop (API Level 21) หรือตั้งแต่ปี 2014
- ตั้งแต่ Android 8.0 Oreo (API Level 26) ขึ้นไป อุปกรณ์แอนดรอยด์จะต้องใช้ CPU แบบ 64-bit เท่านั้น
- Google Play บังคับให้นักพัฒนาปรับปรุงแอปให้รองรับการทำงานบน Android OS แบบ 64-bit มาตั้งแต่ปี 2019
จึงทำให้ในปัจจุบันอุปกรณ์แอนดรอยด์ใช้ CPU แบบ 64-bit เป็นที่เรียบร้อยแล้ว ไม่ว่าจะเป็น Smartphone หรือ Tablet ก็ตาม
อุปกรณ์แอนดรอยด์ที่ยังใช้ CPU แบบ 32-bit อยู่ก็จะมีแค่ Android Wear หรือ Android TV แค่บางรุ่นเท่านั้น หรืออาจจะเป็นอุปกรณ์แอนดรอยด์ที่ใช้ในภาคอุตสาหกรรมเฉพาะทาง
และแอปที่ใช้คำสั่งของ Android Platform API ก็จะรองรับการทำงานบน Android OS แบบ 64-bit โดยไม่ต้องทำอะไรเพิ่ม แต่สำหรับแอปที่มีการใช้ Android NDK หรือไฟล์ .so
ก็จะต้องทำให้รองรับด้วย
เกร็ดความรู้ – โดยปกติแล้ว Android OS แบบ 64-bit จะรองรับการทำงานแบบ 32-bit ด้วย แต่ Google Pixel 7 และ Google Pixel 7 Pro เป็นอุปกรณ์แอนดรอยด์กลุ่มแรกที่เลิกรองรับการทำงานแบบ 32-bit [อ้างอิง]
Real Android Device กับ Android Emulator
ในปัจจุบันอุปกรณ์แอนดรอยด์อย่าง Smartphone หรือ Tablet ล้วนใช้ CPU เป็น ARM กันหมดแล้ว จึงเป็นเรื่องปกติที่บางแอปอาจจะถูกพัฒนาเพื่อใช้งานบน CPU ที่เป็น ARM โดยเฉพาะ (เช่น ใช้ Native Library ที่รองรับเฉพาะ ARM เท่านั้น) และนั่นก็อาจจะเป็นปัญหาสำหรับนักพัฒนาที่ทดสอบแอปเหล่านั้นบน Android Emulator ก็เป็นได้
เกร็ดความรู้ - Asus Zenfone 2 ที่วางจำหน่ายเมื่อปี 2015 เป็น Android Smartphone รุ่นสุดท้ายที่เป็น x86 (ใช้ CPU เป็น Intel Atom Z3560)
เพราะ System Image ของ Android Emulator นั้นมีให้เลือกทั้ง ARM และ x86 โดยขึ้นอยู่กับคอมพิวเตอร์ที่ใช้ ด้วยเหตุผลที่ว่าประสิทธิภาพในการทำงานของ Android Emulator จะดีที่สุดถ้าเลือก System Image ที่มี ABI ตรงกับ CPU ของคอมพิวเตอร์ที่ใช้
ยกตัวอย่างเช่น
- คอมพิวเตอร์ที่ใช้ CPU ตระกูล Intel หรือ AMD ควรเลือก System Image ที่ ABI เป็นแบบ x86
- คอมพิวเตอร์ที่ใช้ CPU ตระกูล Apple Silicon ควรเลือก System Image ที่ ABI เป็นแบบ ARM
อย่างน้อยก็จนกว่า Intel กับ AMD จะทำ CPU ที่เป็น ARM น่ะนะ
ด้วยเหตุนี้จึงทำให้การพัฒนาแอปบนคอมพิวเตอร์ที่ใช้ CPU เป็น Intel หรือ AMD ค่อนข้างเสียเปรียบเมื่อพัฒนาแอปที่มีการใช้ Native Library ที่รองรับเฉพาะ ARM เท่านั้น เพราะทดสอบแอปบน Android Emulator ที่ใช้ System Image เป็น x86 ไม่ได้ จะเปลี่ยนไปใช้ System Image ที่เป็น ARM ก็เจอปัญหาว่า Android Emulator ทำงานช้ามากกกกกก
และนั่นจึงเป็นข้อได้เปรียบสำหรับ CPU ตระกูล Apple Silicon อย่าง M1 หรือ M2 เพราะจะได้ใช้ Android Emulator ที่มี ABI เป็น ARM ซึ่งสอดคล้องกับ Smartphone และ Tablet ที่ใช้งานจริง
นักพัฒนาจะแก้ปัญหาด้วยการใช้ Android Emulator ที่คนทั่วไปใช้เล่นเกมอย่าง BlueStacks หรือ LD Player ก็ได้เช่นกัน เพราะมี ARM Translation ที่จะทำให้ใช้ ABI เป็น ARM บนคอมพิวเตอร์ที่ใช้ CPU เป็น x86 ได้อย่างลื่นไหล
แต่โปรแกรมเหล่านี้ไม่ได้สร้างขึ้นมาเพื่อใช้ในการพัฒนาแอป จึงทำให้ขาดความสามารถหลายอย่างที่เอื้อต่อการพัฒนาแอปเมื่อเทียบกับ Android Emulator ที่มากับ Android Studio หรือ Genymotion
สรุป
สำหรับการพัฒนาแอปโดยทั่วไปที่มีแต่การเรียกใช้คำสั่งจาก Android Platform API จะสามารถมองข้ามเรื่อง CPU Architecture และ Android ABI ไปได้เลย แต่สำหรับแอปที่มีการใช้งาน Android NDK หรือ Native Library จะมีการทำงานที่อาจจะขึ้นอยู่กับ ABI ด้วย
โดยเฉพาะการทดสอบแอปเหล่านี้บน Android Emulator ที่นักพัฒนาควรเข้าใจและรู้ว่าจะต้องทดสอบแอปบน Android Emulator แบบไหน และเลือกใช้งาน Native Library ที่รองรับกับ ABI แบบไหนด้วย