Android 10 เพิ่มการรองรับ Android Interface Definition Language (AIDL) ที่เสถียร ซึ่งเป็นวิธีใหม่ในการติดตาม Application Program Interface (API) และ Application Binary Interface (ABI) ที่อินเทอร์เฟซ AIDL มีให้ Stable AIDL ทำงานเหมือนกับ AIDL ทุกประการ แต่ระบบบิลด์จะติดตาม ความเข้ากันได้ของอินเทอร์เฟซ และมีข้อจำกัดเกี่ยวกับสิ่งที่คุณทำได้ ดังนี้
- อินเทอร์เฟซจะกำหนดไว้ในระบบบิลด์ด้วย
aidl_interfaces
- อินเทอร์เฟซมีได้เฉพาะ Structured Data ระบบจะสร้าง Parcelable ที่แสดงถึง ประเภทที่ต้องการโดยอัตโนมัติตามคำจำกัดความ AIDL และ จะจัดรูปแบบและยกเลิกการจัดรูปแบบโดยอัตโนมัติ
- อินเทอร์เฟซสามารถประกาศเป็นแบบเสถียร (เข้ากันได้แบบย้อนหลัง) เมื่อเกิดเหตุการณ์นี้ ระบบจะติดตามและกำหนดเวอร์ชัน API ของแอปในไฟล์ที่อยู่ข้างอินเทอร์เฟซ AIDL
AIDL ที่มีโครงสร้างเทียบกับ AIDL ที่เสถียร
Structured AIDL หมายถึงประเภทที่กำหนดใน AIDL เท่านั้น เช่น การประกาศที่ส่งผ่านได้ (ที่ส่งผ่านได้ที่กำหนดเอง) ไม่ใช่ AIDL ที่มีโครงสร้าง Parcelable ที่มีฟิลด์ที่กำหนดใน AIDL เรียกว่า Parcelable ที่มีโครงสร้าง
AIDL ที่เสถียรต้องใช้ AIDL ที่มีโครงสร้างเพื่อให้ระบบบิลด์และคอมไพเลอร์
สามารถเข้าใจได้ว่าการเปลี่ยนแปลงที่ทำกับ Parcelable เข้ากันได้แบบย้อนหลังหรือไม่
อย่างไรก็ตาม อินเทอร์เฟซที่มีโครงสร้างบางอย่างอาจไม่เสถียร อินเทอร์เฟซต้องใช้เฉพาะประเภทที่มีโครงสร้างและต้องใช้ฟีเจอร์การกำหนดเวอร์ชันต่อไปนี้จึงจะเสถียร
ในทางกลับกัน อินเทอร์เฟซจะไม่เสถียรหากใช้ระบบบิลด์หลัก
ในการสร้าง หรือหากตั้งค่า unstable:true
กำหนดอินเทอร์เฟซ AIDL
คำจำกัดความของ aidl_interface
มีลักษณะดังนี้
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions_with_info: [
{
version: "1",
imports: ["other-aidl-V1"],
},
{
version: "2",
imports: ["other-aidl-V3"],
}
],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
rust: {
enabled: true,
},
},
}
name
: ชื่อของโมดูลอินเทอร์เฟซ AIDL ที่ระบุอินเทอร์เฟซ AIDL แต่ละรายการโดยไม่ซ้ำกันsrcs
: รายการไฟล์ต้นฉบับ AIDL ที่ประกอบกันเป็นอินเทอร์เฟซ เส้นทาง สำหรับประเภท AIDLFoo
ที่กำหนดไว้ในแพ็กเกจcom.acme
ควรอยู่ที่<base_path>/com/acme/Foo.aidl
โดย<base_path>
อาจเป็นไดเรกทอรีใดก็ได้ ที่เกี่ยวข้องกับไดเรกทอรีที่Android.bp
อยู่ ในตัวอย่างก่อนหน้านี้<base_path>
คือsrcs/aidl
local_include_dir
: เส้นทางที่ชื่อแพ็กเกจเริ่มต้น ซึ่งสอดคล้องกับ<base_path>
ที่อธิบายไว้ข้างต้นimports
: รายการโมดูลaidl_interface
ที่ใช้ หากอินเทอร์เฟซ AIDL รายการใดรายการหนึ่งของคุณใช้อินเทอร์เฟซหรือ Parcelable จากaidl_interface
อื่น ให้ระบุชื่อที่นี่ ซึ่งอาจเป็นชื่อเพียงอย่างเดียว เพื่ออ้างอิงถึงเวอร์ชันล่าสุด หรือชื่อที่มีคำต่อท้ายเวอร์ชัน (เช่น-V1
) เพื่ออ้างอิงถึง เวอร์ชันที่เฉพาะเจาะจง ระบบรองรับการระบุเวอร์ชันตั้งแต่ Android 12versions
: อินเทอร์เฟซเวอร์ชันก่อนหน้าซึ่ง หยุดการอัปเดตภายใต้api_dir
ตั้งแต่ Android 11 เป็นต้นไปversions
จะหยุดการอัปเดตภายใต้aidl_api/name
หากไม่มีอินเทอร์เฟซเวอร์ชันที่หยุดการเปลี่ยนแปลงแล้ว คุณไม่ควรระบุค่านี้ และจะไม่มีการตรวจสอบความเข้ากันได้ ช่องนี้ถูกแทนที่ด้วยversions_with_info
สำหรับ Android 13 ขึ้นไปversions_with_info
: รายการของทูเพิล ซึ่งแต่ละทูเพิลมีชื่อของ เวอร์ชันที่ตรึงไว้และรายการที่มีการนำเข้าเวอร์ชันของโมดูล aidl_interface อื่นๆ ที่ aidl_interface เวอร์ชันนี้นำเข้า คำจำกัดความ ของอินเทอร์เฟซ AIDL เวอร์ชัน V IFACE อยู่ที่aidl_api/IFACE/V
ฟิลด์นี้เปิดตัวใน Android 13 และไม่ควรแก้ไขในAndroid.bp
โดยตรง ระบบจะเพิ่มหรืออัปเดตฟิลด์โดยการเรียกใช้*-update-api
หรือ*-freeze-api
นอกจากนี้ ระบบจะย้ายข้อมูลversions
โดยอัตโนมัติไปยังversions_with_info
เมื่อผู้ใช้เรียกใช้*-update-api
หรือ*-freeze-api
stability
: แฟล็กที่ไม่บังคับสำหรับสัญญาความเสถียรของอินเทอร์เฟซนี้ โดยรองรับเฉพาะ"vintf"
หากไม่ได้ตั้งค่าstability
ไว้ ระบบบิลด์จะตรวจสอบว่าอินเทอร์เฟซเข้ากันได้แบบย้อนหลังหรือไม่ เว้นแต่จะระบุunstable
ไว้ การไม่ตั้งค่าจะสอดคล้องกับอินเทอร์เฟซที่มี ความเสถียรภายในบริบทการคอมไพล์นี้ (ดังนั้นไม่ว่าจะเป็นสิ่งต่างๆ ของระบบทั้งหมด เช่น สิ่งต่างๆ ในsystem.img
และพาร์ติชันที่เกี่ยวข้อง หรือสิ่งต่างๆ ของผู้ให้บริการทั้งหมด เช่น สิ่งต่างๆ ในvendor.img
และพาร์ติชันที่เกี่ยวข้อง) หากตั้งค่าstability
เป็น"vintf"
แสดงว่าเป็นการรับประกันความเสถียร อินเทอร์เฟซต้องมีความเสถียรตราบใดที่ยังมีการใช้งานgen_trace
: แฟล็กที่ไม่บังคับเพื่อเปิดหรือปิดการติดตาม โดยเริ่มตั้งแต่ Android 14 ค่าเริ่มต้นคือtrue
สำหรับแบ็กเอนด์cpp
และjava
host_supported
: แฟล็กที่ไม่บังคับซึ่งเมื่อตั้งค่าเป็นtrue
จะทำให้ไลบรารีที่สร้างขึ้นพร้อมใช้งานในสภาพแวดล้อมโฮสต์unstable
: แฟล็กที่ไม่บังคับซึ่งใช้เพื่อทำเครื่องหมายว่าอินเทอร์เฟซนี้ไม่ จำเป็นต้องเสถียร เมื่อตั้งค่าเป็นtrue
ระบบบิลด์จะไม่สร้างการดัมพ์ API สำหรับอินเทอร์เฟซและไม่จำเป็นต้องอัปเดตfrozen
: ค่าสถานะที่ไม่บังคับซึ่งเมื่อตั้งค่าเป็นtrue
หมายความว่าอินเทอร์เฟซ ไม่มีการเปลี่ยนแปลงใดๆ นับตั้งแต่เวอร์ชันก่อนหน้าของอินเทอร์เฟซ ซึ่งจะช่วยให้ ตรวจสอบได้มากขึ้นในเวลาบิลด์ เมื่อตั้งค่าเป็นfalse
แสดงว่าอินเทอร์เฟซอยู่ระหว่างการพัฒนาและมีการเปลี่ยนแปลงใหม่ ดังนั้นการเรียกใช้foo-freeze-api
จะสร้างเวอร์ชันใหม่และเปลี่ยนค่าเป็นtrue
โดยอัตโนมัติ เปิดตัวใน Android 14backend.<type>.enabled
: แฟล็กเหล่านี้จะสลับแต่ละแบ็กเอนด์ที่คอมไพเลอร์ AIDL สร้างโค้ดให้ ระบบรองรับแบ็กเอนด์ 4 รายการ ได้แก่ Java, C++, NDK และ Rust แบ็กเอนด์ Java, C++ และ NDK จะเปิดใช้ โดยค่าเริ่มต้น หากไม่จำเป็นต้องใช้แบ็กเอนด์ทั้ง 3 รายการนี้ คุณจะต้อง ปิดใช้แบ็กเอนด์อย่างชัดแจ้ง Rust จะปิดใช้โดยค่าเริ่มต้นจนกว่าจะถึง Android 15backend.<type>.apex_available
: รายชื่อชื่อ APEX ที่มีไลบรารี Stub ที่สร้างขึ้นbackend.[cpp|java].gen_log
: แฟล็กที่ไม่บังคับซึ่งควบคุมว่าจะสร้างโค้ดเพิ่มเติมเพื่อรวบรวมข้อมูลเกี่ยวกับธุรกรรมหรือไม่backend.[cpp|java].vndk.enabled
: แฟล็กที่ไม่บังคับเพื่อทําให้อินเทอร์เฟซนี้ เป็นส่วนหนึ่งของ VNDK ค่าเริ่มต้นคือfalse
backend.[cpp|ndk].additional_shared_libraries
: เปิดตัวใน Android 14 แฟล็กนี้จะเพิ่มการอ้างอิงไปยัง ไลบรารีเนทีฟ แฟล็กนี้มีประโยชน์กับndk_header
และcpp_header
backend.java.sdk_version
: แฟล็กที่ไม่บังคับสําหรับการระบุเวอร์ชัน ของ SDK ที่สร้างไลบรารี Stub ของ Java ค่าเริ่มต้นคือ"system_current"
ไม่ควรตั้งค่านี้เมื่อbackend.java.platform_apis
เป็นtrue
backend.java.platform_apis
: แฟล็กที่ไม่บังคับซึ่งควรตั้งค่าเป็นtrue
เมื่อไลบรารีที่สร้างขึ้นต้องสร้างเทียบกับ API ของแพลตฟอร์ม แทนที่จะเป็น SDK
ระบบจะสร้างไลบรารี Stub สำหรับชุดค่าผสมของเวอร์ชันและแบ็กเอนด์ที่เปิดใช้แต่ละชุด ดูวิธีอ้างอิงไลบรารี Stub เวอร์ชันที่เฉพาะเจาะจง สำหรับแบ็กเอนด์ที่เฉพาะเจาะจงได้ที่กฎการตั้งชื่อโมดูล
เขียนไฟล์ AIDL
อินเทอร์เฟซใน AIDL ที่เสถียรจะคล้ายกับอินเทอร์เฟซแบบเดิม ยกเว้นว่าจะไม่ได้รับอนุญาตให้ใช้ Parcelable ที่ไม่มีโครงสร้าง (เนื่องจากไม่เสถียร โปรดดูAIDL ที่มีโครงสร้างเทียบกับ AIDL ที่เสถียร) ความแตกต่างหลักใน AIDL ที่เสถียรคือวิธีกำหนด Parcelable ก่อนหน้านี้มีการประกาศล่วงหน้าสำหรับ Parcelable ใน AIDL ที่ไม่เสถียร (และมีโครงสร้าง) ฟิลด์และตัวแปร Parcelable จะได้รับการกำหนดอย่างชัดเจน
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
ระบบรองรับค่าเริ่มต้น (แต่ไม่บังคับ) สำหรับ boolean
, char
,
float
, double
, byte
, int
, long
และ String
ใน Android
12 ยังรองรับค่าเริ่มต้นสำหรับการแจงนับที่ผู้ใช้กำหนดด้วย เมื่อไม่ได้ระบุค่าเริ่มต้น ระบบจะใช้ค่าที่คล้ายกับ 0 หรือค่าว่าง
การแจงนับที่ไม่มีค่าเริ่มต้นจะเริ่มต้นเป็น 0 แม้ว่าจะไม่มีตัวแจงนับเป็น 0 ก็ตาม
ใช้ไลบรารี Stub
หลังจากเพิ่มไลบรารี Stub เป็นทรัพยากร Dependency ในโมดูลแล้ว คุณ
จะรวมไลบรารีเหล่านั้นไว้ในไฟล์ได้ ตัวอย่างไลบรารี Stub ใน
ระบบบิลด์ (Android.mk
ใช้กับการกําหนดโมดูลเดิมได้ด้วย)
โปรดทราบว่าในตัวอย่างเหล่านี้ไม่มีเวอร์ชัน จึงแสดงถึงการใช้อินเทอร์เฟซที่ไม่เสถียร แต่ชื่อของอินเทอร์เฟซที่มีเวอร์ชันจะมีข้อมูลเพิ่มเติม โปรดดูการกำหนดเวอร์ชันของอินเทอร์เฟซ
cc_... {
name: ...,
// use `shared_libs:` to load your library and its transitive dependencies
// dynamically
shared_libs: ["my-module-name-cpp"],
// use `static_libs:` to include the library in this binary and drop
// transitive dependencies
static_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// use `static_libs:` to add all jars and classes to this jar
static_libs: ["my-module-name-java"],
// use `libs:` to make these classes available during build time, but
// not add them to the jar, in case the classes are already present on the
// boot classpath (such as if it's in framework.jar) or another jar.
libs: ["my-module-name-java"],
// use `srcs:` with `-java-sources` if you want to add classes in this
// library jar directly, but you get transitive dependencies from
// somewhere else, such as the boot classpath or another jar.
srcs: ["my-module-name-java-source", ...],
...
}
# or
rust_... {
name: ...,
rustlibs: ["my-module-name-rust"],
...
}
ตัวอย่างใน C++
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
ตัวอย่างใน Java
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
ตัวอย่างใน Rust
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
อินเทอร์เฟซการกำหนดเวอร์ชัน
การประกาศโมดูลที่มีชื่อ foo จะสร้างเป้าหมายในระบบบิลด์ด้วย
ซึ่งคุณใช้เพื่อจัดการ API ของโมดูลได้ เมื่อสร้าง foo-freeze-api
จะเพิ่มคำจำกัดความ API ใหม่ภายใต้ api_dir
หรือ
aidl_api/name
ขึ้นอยู่กับเวอร์ชัน Android และ
เพิ่มไฟล์ .hash
ซึ่งทั้ง 2 อย่างแสดงถึงเวอร์ชันที่เพิ่งตรึงของ
อินเทอร์เฟซ foo-freeze-api ยังอัปเดตพร็อพเพอร์ตี้ versions_with_info
เพื่อแสดงเวอร์ชันเพิ่มเติมและ imports
สำหรับเวอร์ชันด้วย โดยพื้นฐานแล้ว
imports
ใน versions_with_info
จะคัดลอกจากฟิลด์ imports
แต่มีการระบุเวอร์ชันเสถียรล่าสุดใน imports
ใน versions_with_info
สำหรับการนำเข้า ซึ่งไม่มีเวอร์ชันที่ชัดเจน
หลังจากระบุพร็อพเพอร์ตี้ versions_with_info
แล้ว ระบบบิลด์จะเรียกใช้การตรวจสอบความเข้ากันได้ระหว่างเวอร์ชันที่หยุดการอัปเดต และระหว่าง Top of Tree (ToT) กับเวอร์ชันที่หยุดการอัปเดตล่าสุด
นอกจากนี้ คุณยังต้องจัดการคำจำกัดความ API ของเวอร์ชัน ToT ด้วย เมื่อใดก็ตามที่มีการอัปเดต API ให้เรียกใช้ foo-update-api เพื่ออัปเดต
aidl_api/name/current
ซึ่งมีคำจำกัดความ API ของเวอร์ชัน ToT
เจ้าของสามารถเพิ่มรายการใหม่ต่อไปนี้เพื่อรักษาความเสถียรของอินเทอร์เฟซ
- วิธีการที่ส่วนท้ายของอินเทอร์เฟซ (หรือวิธีการที่มีการกำหนดซีเรียลใหม่ อย่างชัดเจน)
- องค์ประกอบที่ท้ายของ Parcelable (ต้องเพิ่มค่าเริ่มต้นสำหรับแต่ละองค์ประกอบ)
- ค่าคงที่
- ใน Android 11 ตัวแจงนับ
- ใน Android 12 ฟิลด์ที่ส่วนท้ายของสหภาพ
ไม่อนุญาตให้ดำเนินการอื่นๆ และไม่มีใครแก้ไขอินเทอร์เฟซได้ (มิฉะนั้นอาจเกิดการทับซ้อนกับการเปลี่ยนแปลงที่เจ้าของทำ)
หากต้องการทดสอบว่าอินเทอร์เฟซทั้งหมดหยุดการเปลี่ยนแปลงเพื่อการเผยแพร่แล้ว ให้สร้างโดยตั้งค่าตัวแปรสภาพแวดล้อมต่อไปนี้
AIDL_FROZEN_REL=true m ...
- บิลด์กำหนดให้อินเทอร์เฟซ AIDL ที่เสถียรทั้งหมดต้อง ได้รับการตรึงซึ่งไม่มีการระบุฟิลด์owner:
AIDL_FROZEN_OWNERS="aosp test"
- build กำหนดให้ต้องตรึงอินเทอร์เฟซ AIDL ที่เสถียรทั้งหมด โดยระบุฟิลด์owner:
เป็น "aosp" หรือ "test"
ความเสถียรของการนำเข้า
การอัปเดตเวอร์ชันของการนำเข้าสำหรับเวอร์ชันที่ตรึงของอินเทอร์เฟซจะ เข้ากันได้แบบย้อนหลังที่เลเยอร์ AIDL ที่เสถียร อย่างไรก็ตาม การอัปเดตเหล่านี้ต้อง อัปเดตเซิร์ฟเวอร์และไคลเอ็นต์ทั้งหมดที่ใช้อินเทอร์เฟซเวอร์ชันก่อนหน้า และแอปบางแอปอาจสับสนเมื่อใช้เวอร์ชันประเภทต่างๆ ร่วมกัน โดยทั่วไปแล้ว สำหรับแพ็กเกจประเภทเท่านั้นหรือแพ็กเกจทั่วไป การดำเนินการนี้จะปลอดภัยเนื่องจากต้องเขียนโค้ดเพื่อจัดการประเภทที่ไม่รู้จักจากธุรกรรม IPC อยู่แล้ว
ในโค้ดแพลตฟอร์ม Android android.hardware.graphics.common
เป็นตัวอย่างที่ใหญ่ที่สุด
ของการอัปเกรดเวอร์ชันประเภทนี้
ใช้อินเทอร์เฟซที่มีการควบคุมเวอร์ชัน
วิธีการเชื่อมต่อ
เมื่อรันไทม์ ขณะพยายามเรียกใช้เมธอดใหม่ในเซิร์ฟเวอร์เก่า ไคลเอ็นต์ใหม่จะได้รับข้อผิดพลาดหรือข้อยกเว้น ทั้งนี้ขึ้นอยู่กับแบ็กเอนด์
cpp
แบ็กเอนด์จะได้รับ::android::UNKNOWN_TRANSACTION
ndk
แบ็กเอนด์จะได้รับSTATUS_UNKNOWN_TRANSACTION
java
แบ็กเอนด์จะได้รับandroid.os.RemoteException
พร้อมข้อความที่ระบุว่าไม่ได้ใช้ API
ดูกลยุทธ์ในการจัดการปัญหานี้ได้ที่หัวข้อ การค้นหาเวอร์ชันและ การใช้ค่าเริ่มต้น
Parcelables
เมื่อมีการเพิ่มฟิลด์ใหม่ลงใน Parcelable ไคลเอ็นต์และเซิร์ฟเวอร์เก่าจะทิ้งฟิลด์เหล่านั้น เมื่อไคลเอ็นต์และเซิร์ฟเวอร์ใหม่ได้รับ Parcelable เก่า ระบบจะป้อนค่าเริ่มต้นสำหรับฟิลด์ใหม่โดยอัตโนมัติ ซึ่งหมายความว่าต้อง ระบุค่าเริ่มต้นสำหรับฟิลด์ใหม่ทั้งหมดใน Parcelable
ไคลเอ็นต์ไม่ควรคาดหวังให้เซิร์ฟเวอร์ใช้ฟิลด์ใหม่ เว้นแต่จะทราบว่าเซิร์ฟเวอร์ใช้เวอร์ชันที่มีการกำหนดฟิลด์ (ดูการค้นหาเวอร์ชัน)
Enum และค่าคงที่
ในทำนองเดียวกัน ไคลเอ็นต์และเซิร์ฟเวอร์ควรปฏิเสธหรือเพิกเฉยต่อค่าคงที่และตัวแจงนับที่ไม่รู้จักตามความเหมาะสม เนื่องจากอาจมีการเพิ่มค่าดังกล่าวในอนาคต เช่น เซิร์ฟเวอร์ไม่ควรยกเลิกเมื่อได้รับ ตัวแจงนับที่ไม่รู้จัก เซิร์ฟเวอร์ควรไม่สนใจ ตัวแจงนับ หรือส่งคืนบางอย่างเพื่อให้ไคลเอ็นต์ทราบว่าการใช้งานนี้ไม่รองรับ
สหภาพ
การพยายามส่งสหภาพที่มีฟิลด์ใหม่จะล้มเหลวหากผู้รับเป็นเวอร์ชันเก่าและ
ไม่รู้จักฟิลด์ดังกล่าว การใช้งานจะไม่เห็นการรวมกับฟิลด์ใหม่ ระบบจะละเว้นความล้มเหลวหากเป็นธุรกรรมทางเดียว มิฉะนั้นข้อผิดพลาดจะเป็น BAD_VALUE
(สำหรับแบ็กเอนด์ C++ หรือ NDK) หรือ IllegalArgumentException
(สำหรับแบ็กเอนด์ Java) ข้อผิดพลาดจะเกิดขึ้นหากไคลเอ็นต์ส่งชุดยูเนียนไปยังฟิลด์ใหม่ในเซิร์ฟเวอร์เก่า หรือเมื่อไคลเอ็นต์เก่าได้รับยูเนียนจากเซิร์ฟเวอร์ใหม่
จัดการหลายเวอร์ชัน
เนมสเปซของ Linker ใน Android มีได้เฉพาะ aidl
อินเทอร์เฟซเวอร์ชันเดียวเพื่อหลีกเลี่ยงสถานการณ์ที่ประเภท aidl
ที่สร้างขึ้นมีคำจำกัดความหลายรายการ
C++ มีกฎการกำหนด 1 รายการที่กำหนดให้มีคำจำกัดความเพียง 1 รายการ
ของแต่ละสัญลักษณ์
การบิลด์ Android จะแสดงข้อผิดพลาดเมื่อโมดูลขึ้นอยู่กับaidl_interface
ไลบรารีaidl_interface
เดียวกันแต่เป็นเวอร์ชันที่แตกต่างกัน
โมดูลอาจขึ้นต่อกันกับไลบรารีเหล่านี้โดยตรงหรือโดยอ้อมผ่านทรัพยากร Dependency ของทรัพยากร Dependency ข้อผิดพลาดเหล่านี้แสดงกราฟการอ้างอิงจากโมดูลที่ไม่สำเร็จไปยังไลบรารี aidl_interface
เวอร์ชันที่ขัดแย้งกัน
คุณต้องอัปเดตการอ้างอิงทั้งหมดให้มีไลบรารีเหล่านี้ในเวอร์ชันเดียวกัน (โดยปกติจะเป็นเวอร์ชันล่าสุด)
หากโมดูลต่างๆ จำนวนมากใช้ไลบรารีอินเทอร์เฟซ การสร้าง cc_defaults
, java_defaults
และ rust_defaults
สำหรับกลุ่มไลบรารีและกระบวนการที่ต้องใช้เวอร์ชันเดียวกันอาจเป็นประโยชน์ เมื่อเปิดตัวอินเทอร์เฟซเวอร์ชันใหม่ ค่าเริ่มต้นเหล่านั้นจะได้รับการอัปเดต และโมดูลทั้งหมดที่ใช้ค่าเริ่มต้นจะได้รับการอัปเดตพร้อมกัน เพื่อให้มั่นใจว่าโมดูลไม่ได้ใช้อินเทอร์เฟซเวอร์ชันอื่น
cc_defaults {
name: "my.aidl.my-process-group-ndk-shared",
shared_libs: ["my.aidl-V3-ndk"],
...
}
cc_library {
name: "foo",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
cc_binary {
name: "bar",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
เมื่อaidl_interface
โมดูลนำเข้าโมดูลaidl_interface
อื่นๆ จะทำให้เกิดการอ้างอิงเพิ่มเติมที่ต้องใช้เวอร์ชันที่เฉพาะเจาะจงร่วมกัน aidl_interface
สถานการณ์นี้อาจจัดการได้ยากเมื่อมีaidl_interface
โมดูลทั่วไปที่นำเข้าในaidl_interface
โมดูลหลายรายการที่ใช้
ร่วมกันในกระบวนการเดียวกัน
aidl_interfaces_defaults
สามารถใช้เพื่อเก็บคำจำกัดความเดียวของ
การอ้างอิงเวอร์ชันล่าสุดสำหรับ aidl_interface
ที่อัปเดตได้ใน
ที่เดียว และใช้โดยโมดูล aidl_interface
ทั้งหมดที่ต้องการนำเข้า
อินเทอร์เฟซทั่วไปนั้น
aidl_interface_defaults {
name: "android.popular.common-latest-defaults",
imports: ["android.popular.common-V3"],
...
}
aidl_interface {
name: "android.foo",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
aidl_interface {
name: "android.bar",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
การพัฒนาที่อิงตามฟีเจอร์แฟลก
คุณไม่สามารถใช้อินเทอร์เฟซที่อยู่ระหว่างการพัฒนา (ไม่คงที่) ในอุปกรณ์ที่เผยแพร่แล้ว เนื่องจาก เราไม่รับประกันความเข้ากันได้แบบย้อนหลัง
AIDL รองรับการเปลี่ยนกลับรันไทม์สำหรับไลบรารีอินเทอร์เฟซที่ไม่ได้ตรึงเหล่านี้ เพื่อให้สามารถเขียนโค้ดกับเวอร์ชันล่าสุดที่ไม่ได้ตรึงและยังคงใช้ในอุปกรณ์ที่เผยแพร่ได้ ลักษณะการทำงานที่เข้ากันได้แบบย้อนหลังของไคลเอ็นต์จะคล้ายกับลักษณะการทำงานที่มีอยู่ และการติดตั้งใช้งานก็ต้องเป็นไปตามลักษณะการทำงานเหล่านั้นด้วย ดูหัวข้อ ใช้อินเทอร์เฟซที่มีการควบคุมเวอร์ชัน
แฟล็กการสร้าง AIDL
Flag ที่ควบคุมลักษณะการทำงานนี้คือ RELEASE_AIDL_USE_UNFROZEN
ซึ่งกำหนดไว้ใน build/release/build_flags.bzl
true
หมายถึงใช้เวอร์ชันที่ไม่ได้ตรึงของอินเทอร์เฟซในรันไทม์ และ false
หมายถึงไลบรารีของเวอร์ชันที่ไม่ได้ตรึงทั้งหมดจะทำงานเหมือนเวอร์ชันที่ตรึงล่าสุด
คุณสามารถลบล้าง Flag เพื่อtrue
สำหรับ
การพัฒนาในเครื่องได้ แต่ต้องเปลี่ยนกลับเป็น false
ก่อนเผยแพร่ โดยปกติแล้ว
การพัฒนาจะทำด้วยการกำหนดค่าที่มีการตั้งค่าสถานะเป็น true
เมทริกซ์ความเข้ากันได้และไฟล์ Manifest
ออบเจ็กต์อินเทอร์เฟซของผู้ให้บริการ (ออบเจ็กต์ VINTF) จะกำหนด เวอร์ชันที่คาดไว้และเวอร์ชันที่ระบุในทั้ง 2 ด้านของ อินเทอร์เฟซของผู้ให้บริการ
อุปกรณ์ส่วนใหญ่ที่ไม่ใช่ Cuttlefish จะกำหนดเป้าหมายเป็นเมทริกซ์ความเข้ากันได้ล่าสุด
หลังจากที่อินเทอร์เฟซได้รับการตรึงแล้วเท่านั้น ดังนั้นจึงไม่มีความแตกต่างในไลบรารี AIDL
ตาม RELEASE_AIDL_USE_UNFROZEN
เมทริกซ์
ระบบจะเพิ่มอินเทอร์เฟซที่พาร์ทเนอร์เป็นเจ้าของลงในเมทริกซ์ความเข้ากันได้เฉพาะอุปกรณ์หรือเฉพาะผลิตภัณฑ์
ที่อุปกรณ์กำหนดเป้าหมายไว้ในระหว่างการพัฒนา ดังนั้นเมื่อมีการเพิ่มอินเทอร์เฟซเวอร์ชันใหม่ที่ไม่ได้ตรึงไว้ลงในเมทริกซ์ความเข้ากันได้
เวอร์ชันก่อนหน้าที่ตรึงไว้จะต้องยังคงอยู่สำหรับ
RELEASE_AIDL_USE_UNFROZEN=false
คุณจัดการปัญหานี้ได้โดยใช้ไฟล์เมทริกซ์ความเข้ากันได้ที่แตกต่างกันสำหรับการกำหนดค่า RELEASE_AIDL_USE_UNFROZEN
ที่แตกต่างกัน หรืออนุญาตทั้ง 2 เวอร์ชันในไฟล์เมทริกซ์ความเข้ากันได้ไฟล์เดียวที่ใช้ในการกำหนดค่าทั้งหมด
เช่น เมื่อเพิ่มเวอร์ชัน 4 ที่ไม่ได้หยุด ให้ใช้ <version>3-4</version>
เมื่อตรึงเวอร์ชัน 4 แล้ว คุณจะนำเวอร์ชัน 3 ออกจากเมทริกซ์ความเข้ากันได้ได้
เนื่องจากระบบจะใช้เวอร์ชัน 4 ที่ตรึงไว้เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น
false
Manifest
ใน Android 15 มีการเปลี่ยนแปลงใน libvintf
เพื่อ
แก้ไขไฟล์ Manifest ในเวลาบิลด์ตามค่าของ
RELEASE_AIDL_USE_UNFROZEN
ไฟล์ Manifest และส่วนของไฟล์ Manifest จะประกาศว่าบริการใช้เวอร์ชันใดของอินเทอร์เฟซ
เมื่อใช้เวอร์ชันล่าสุดที่ไม่ได้หยุดการอัปเดตของอินเทอร์เฟซ
คุณต้องอัปเดตไฟล์ Manifest เพื่อให้สอดคล้องกับเวอร์ชันใหม่นี้ เมื่อ
RELEASE_AIDL_USE_UNFROZEN=false
ปรับรายการในไฟล์ Manifest โดย
libvintf
เพื่อให้สอดคล้องกับการเปลี่ยนแปลงในไลบรารี AIDL ที่สร้างขึ้น เวอร์ชัน
ได้รับการแก้ไขจากเวอร์ชันที่อัปเดตได้ N
เป็น
เวอร์ชันที่อัปเดตไม่ได้ล่าสุด N - 1
ดังนั้น ผู้ใช้จึงไม่จำเป็นต้องจัดการไฟล์ Manifest หรือส่วนของไฟล์ Manifest หลายรายการสำหรับแต่ละบริการ
การเปลี่ยนแปลงไคลเอ็นต์ HAL
โค้ดไคลเอ็นต์ HAL ต้องเข้ากันได้แบบย้อนหลังกับเวอร์ชันที่รองรับก่อนหน้าแต่ละเวอร์ชันที่หยุดการอัปเดตแล้ว
เมื่อ RELEASE_AIDL_USE_UNFROZEN
false
บริการจะดูเหมือนเวอร์ชันที่หยุดการอัปเดตล่าสุดหรือเวอร์ชันก่อนหน้าเสมอ (เช่น การเรียกใช้เมธอดใหม่ที่ไม่ได้หยุดการอัปเดตจะแสดงผลเป็น UNKNOWN_TRANSACTION
หรือฟิลด์ parcelable
ใหม่จะมีค่าเริ่มต้น) ไคลเอ็นต์เฟรมเวิร์ก Android ต้องเข้ากันได้กับเวอร์ชันก่อนหน้าเพิ่มเติม แต่รายละเอียดนี้เป็นรายละเอียดใหม่สำหรับไคลเอ็นต์ของผู้ให้บริการและไคลเอ็นต์ของอินเทอร์เฟซที่พาร์ทเนอร์เป็นเจ้าของ
การเปลี่ยนแปลงการใช้งาน HAL
ความแตกต่างที่ใหญ่ที่สุดในการพัฒนา HAL ด้วยการพัฒนาที่อิงตาม Flag คือ
ข้อกำหนดสำหรับการใช้งาน HAL ให้เข้ากันได้แบบย้อนหลังกับเวอร์ชันล่าสุด
ที่หยุดการเปลี่ยนแปลงเพื่อทำงานเมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น false
การพิจารณาความเข้ากันได้แบบย้อนหลังในการติดตั้งใช้งานและรหัสอุปกรณ์เป็น
แบบฝึกหัดใหม่ ดูใช้อินเทอร์เฟซที่มีการกำหนดเวอร์ชัน
โดยทั่วไปแล้ว ข้อควรพิจารณาเกี่ยวกับการทำงานร่วมกันกับเวอร์ชันก่อนหน้าจะเหมือนกันสำหรับไคลเอ็นต์และเซิร์ฟเวอร์ รวมถึงสำหรับโค้ดเฟรมเวิร์กและโค้ดของผู้ให้บริการ แต่ก็มีความแตกต่างเล็กน้อยที่คุณต้องทราบ เนื่องจากตอนนี้คุณกำลังใช้ 2 เวอร์ชันที่ใช้ซอร์สโค้ดเดียวกัน (เวอร์ชันปัจจุบันที่ยังไม่หยุดการอัปเดต)
ตัวอย่าง: อินเทอร์เฟซมีเวอร์ชันที่หยุดการเปลี่ยนแปลง 3 เวอร์ชัน อินเทอร์เฟซได้รับการอัปเดตด้วย
วิธีการใหม่ ทั้งไคลเอ็นต์และบริการได้รับการอัปเดตให้ใช้ไลบรารีเวอร์ชัน 4 ใหม่
เนื่องจากไลบรารี V4 อิงตามอินเทอร์เฟซเวอร์ชันที่ไม่ได้ตรึงไว้ จึงทํางานเหมือนเวอร์ชันที่ตรึงไว้ล่าสุด ซึ่งก็คือเวอร์ชัน 3 เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น false
และป้องกันการใช้วิธีการใหม่
เมื่ออินเทอร์เฟซหยุดทำงาน ค่าทั้งหมดของ RELEASE_AIDL_USE_UNFROZEN
จะใช้เวอร์ชันที่หยุดทำงานนั้น และสามารถนำโค้ดที่จัดการความเข้ากันได้แบบย้อนหลังออกได้
เมื่อเรียกใช้เมธอดใน Callback คุณต้องจัดการกรณีที่ระบบแสดงผล UNKNOWN_TRANSACTION
อย่างเหมาะสม ไคลเอ็นต์อาจใช้การเรียกกลับ 2 เวอร์ชันที่แตกต่างกัน
โดยอิงตามการกำหนดค่าการเผยแพร่ ดังนั้นคุณจึงไม่สามารถ
ถือว่าไคลเอ็นต์ส่งเวอร์ชันล่าสุด และเมธอดใหม่อาจแสดงผล
ค่านี้ ซึ่งคล้ายกับวิธีที่ไคลเอ็นต์ AIDL ที่เสถียรรักษาความเข้ากันได้แบบย้อนหลังกับเซิร์ฟเวอร์ที่อธิบายไว้ในใช้อินเทอร์เฟซที่มีการควบคุมเวอร์ชัน
// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
mMyCallback = cb;
// Get the version of the callback for later when we call methods on it
auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
return status;
}
// Example of using the callback later
void NotifyCallbackLater() {
// From the latest frozen version (V2)
mMyCallback->foo();
// Call this method from the unfrozen V3 only if the callback is at least V3
if (mMyCallbackVersion >= 3) {
mMyCallback->bar();
}
}
ฟิลด์ใหม่ในประเภทที่มีอยู่ (parcelable
, enum
, union
) อาจไม่มีอยู่หรือมีค่าเริ่มต้นเมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น false
และระบบจะทิ้งค่าของฟิลด์ใหม่ที่บริการพยายามส่งออกนอกกระบวนการ
คุณจะส่งหรือรับประเภทใหม่ที่เพิ่มในเวอร์ชันที่เลิกตรึงนี้ผ่านอินเทอร์เฟซไม่ได้
การติดตั้งใช้งานจะไม่ได้รับการเรียกใช้เมธอดใหม่จากไคลเอ็นต์ใดๆ เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น false
โปรดระมัดระวังในการใช้ตัวแจงนับใหม่กับเวอร์ชันที่เปิดตัวเท่านั้น และไม่ใช่เวอร์ชันก่อนหน้า
โดยปกติแล้ว คุณจะใช้ foo->getInterfaceVersion()
เพื่อดูว่าอินเทอร์เฟซระยะไกลใช้เวอร์ชันใด แต่การรองรับการกำหนดเวอร์ชันตาม Flag จะทำให้คุณ
ใช้ 2 เวอร์ชันที่แตกต่างกัน ดังนั้นคุณอาจต้องดูเวอร์ชันของ
อินเทอร์เฟซปัจจุบัน คุณทำได้โดยรับเวอร์ชันอินเทอร์เฟซของ
ออบเจ็กต์ปัจจุบัน เช่น this->getInterfaceVersion()
หรือวิธีอื่นๆ สำหรับ my_ver
ดูข้อมูลเพิ่มเติมได้ที่การค้นหาเวอร์ชันอินเทอร์เฟซของออบเจ็กต์ระยะไกล
อินเทอร์เฟซที่เสถียรของ VINTF ใหม่
เมื่อมีการเพิ่มแพ็กเกจอินเทอร์เฟซ AIDL ใหม่ จะไม่มีเวอร์ชันที่หยุดการเปลี่ยนแปลงล่าสุด ดังนั้น
จึงไม่มีลักษณะการทำงานที่จะใช้เป็นข้อมูลสำรองเมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น
false
อย่าใช้อินเทอร์เฟซเหล่านี้ เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น
false
Service Manager จะไม่อนุญาตให้ลงทะเบียนอินเทอร์เฟซ
และไคลเอ็นต์จะไม่พบอินเทอร์เฟซดังกล่าว
คุณเพิ่มบริการแบบมีเงื่อนไขได้โดยอิงตามค่าของ
RELEASE_AIDL_USE_UNFROZEN
แฟล็กในไฟล์ Makefile ของอุปกรณ์
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
หากบริการเป็นส่วนหนึ่งของกระบวนการที่ใหญ่กว่านั้น คุณจึงเพิ่มบริการลงในอุปกรณ์แบบมีเงื่อนไขไม่ได้ ให้ตรวจสอบว่ามีการประกาศบริการด้วย
IServiceManager::isDeclared()
หรือไม่ หากมีการประกาศและลงทะเบียนไม่สำเร็จ ให้
ยกเลิกกระบวนการ หากไม่ได้ประกาศไว้ การลงทะเบียนก็อาจไม่สำเร็จ
อินเทอร์เฟซส่วนขยายที่เสถียรของ VINTF ใหม่
อินเทอร์เฟซส่วนขยายใหม่
ไม่มีเวอร์ชันก่อนหน้าให้ย้อนกลับ และเนื่องจากไม่ได้
ลงทะเบียนกับ ServiceManager
หรือประกาศในไฟล์ Manifest ของ VINTF
จึงใช้ IServiceManager::isDeclared()
เพื่อกำหนดเวลาที่จะแนบ
อินเทอร์เฟซส่วนขยายกับอินเทอร์เฟซอื่นไม่ได้
ตัวแปร RELEASE_AIDL_USE_UNFROZEN
สามารถใช้เพื่อพิจารณาว่าจะ
แนบอินเทอร์เฟซส่วนขยายใหม่ที่เลิกตรึงกับอินเทอร์เฟซที่มีอยู่หรือไม่ เพื่อหลีกเลี่ยง
การใช้อินเทอร์เฟซดังกล่าวในอุปกรณ์ที่เผยแพร่ ต้องหยุดการอัปเดตอินเทอร์เฟซเพื่อใช้ในอุปกรณ์ที่เปิดตัวแล้ว
การทดสอบ vts_treble_vintf_vendor_test
และ vts_treble_vintf_framework_test
VTS
จะตรวจหาเมื่อมีการใช้อินเทอร์เฟซส่วนขยายที่ไม่ได้ตรึงในอุปกรณ์ที่เผยแพร่แล้ว
และแสดงข้อผิดพลาด
หากอินเทอร์เฟซส่วนขยายไม่ใช่เวอร์ชันใหม่และมีเวอร์ชันที่หยุดไว้ก่อนหน้านี้ ระบบจะเปลี่ยนกลับไปใช้เวอร์ชันที่หยุดไว้ก่อนหน้านี้และไม่ต้องทำตามขั้นตอนเพิ่มเติม
Cuttlefish เป็นเครื่องมือพัฒนา
ทุกปีหลังจากที่ VINTF หยุดการเปลี่ยนแปลง เราจะปรับเมทริกซ์ความเข้ากันได้ของเฟรมเวิร์ก (FCM) target-level
และPRODUCT_SHIPPING_API_LEVEL
ของ Cuttlefish
เพื่อให้สอดคล้องกับอุปกรณ์ที่เปิดตัวพร้อมกับรุ่นของปีถัดไป เราปรับ
target-level
และPRODUCT_SHIPPING_API_LEVEL
เพื่อให้มั่นใจว่ามี
อุปกรณ์ที่เปิดตัวซึ่งผ่านการทดสอบและเป็นไปตามข้อกำหนดใหม่สำหรับการเปิดตัวในปีหน้า
เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น true
ระบบจะใช้ Cuttlefish
ในการพัฒนา Android เวอร์ชันในอนาคต โดยมีเป้าหมายเป็นระดับ FCM ของ Android
รุ่นที่จะเปิดตัวในปีหน้าและ PRODUCT_SHIPPING_API_LEVEL
ซึ่งกำหนดให้เป็นไปตาม
ข้อกำหนดด้านซอฟต์แวร์ของผู้ให้บริการ (VSR) ของรุ่นถัดไป
เมื่อ RELEASE_AIDL_USE_UNFROZEN
เป็น false
Cuttlefish จะมี target-level
และ PRODUCT_SHIPPING_API_LEVEL
ก่อนหน้าเพื่อแสดงอุปกรณ์รุ่นที่เผยแพร่
ใน Android 14 และเวอร์ชันที่ต่ำกว่า การแยกความแตกต่างนี้จะทำได้ด้วยสาขา Git ที่แตกต่างกันซึ่งจะไม่รับการเปลี่ยนแปลงใน FCM
target-level
, ระดับ API การจัดส่ง หรือโค้ดอื่นๆ ที่กำหนดเป้าหมายเป็นการเผยแพร่ครั้งถัดไป
กฎการตั้งชื่อโมดูล
ใน Android 11 ระบบจะสร้างโมดูลไลบรารี Stub โดยอัตโนมัติสำหรับแต่ละชุดค่าผสมของเวอร์ชันและ
แบ็กเอนด์ที่เปิดใช้ หากต้องการอ้างอิงโมดูลไลบรารี Stub ที่เฉพาะเจาะจงสำหรับการลิงก์ อย่าใช้ชื่อโมดูล aidl_interface
แต่ให้ใช้ชื่อโมดูลไลบรารี Stub ซึ่งคือ ifacename-version-backend โดยที่
ifacename
: ชื่อของโมดูลaidl_interface
version
เป็นอย่างใดอย่างหนึ่งต่อไปนี้Vversion-number
สำหรับเวอร์ชันที่อัปเดตไม่ได้Vlatest-frozen-version-number + 1
สำหรับเวอร์ชัน tip-of-tree (ยังไม่ได้อัปเดต)
backend
เป็นอย่างใดอย่างหนึ่งต่อไปนี้java
สำหรับแบ็กเอนด์ Javacpp
สำหรับแบ็กเอนด์ C++ndk
หรือndk_platform
สำหรับแบ็กเอนด์ NDK โดยรายการแรกมีไว้สำหรับแอป และรายการที่สองมีไว้สำหรับการใช้งานแพลตฟอร์มจนถึง Android 13 ใน Android 13 ขึ้นไป ให้ใช้เฉพาะndk
rust
สำหรับแบ็กเอนด์ Rust
สมมติว่ามีโมดูลชื่อ foo และเวอร์ชันล่าสุดคือ 2 และรองรับทั้ง NDK และ C++ ในกรณีนี้ AIDL จะสร้างโมดูลต่อไปนี้
- อิงตามเวอร์ชัน 1
foo-V1-(java|cpp|ndk|ndk_platform|rust)
- อิงตามเวอร์ชัน 2 (เวอร์ชันเสถียรล่าสุด)
foo-V2-(java|cpp|ndk|ndk_platform|rust)
- อิงตามเวอร์ชัน ToT
foo-V3-(java|cpp|ndk|ndk_platform|rust)
เมื่อเทียบกับ Android 11
foo-backend
ซึ่งอ้างอิงถึงเวอร์ชันเสถียรล่าสุด จะกลายเป็นfoo-V2-backend
foo-unstable-backend
ซึ่งอ้างอิงถึง ToT version จะกลายเป็นfoo-V3-backend
ชื่อไฟล์เอาต์พุตจะเหมือนกับชื่อโมดูลเสมอ
- อิงตามเวอร์ชัน 1:
foo-V1-(cpp|ndk|ndk_platform|rust).so
- อิงตามเวอร์ชัน 2:
foo-V2-(cpp|ndk|ndk_platform|rust).so
- อิงตามเวอร์ชันของ ToT:
foo-V3-(cpp|ndk|ndk_platform|rust).so
โปรดทราบว่าคอมไพเลอร์ AIDL ไม่ได้สร้างทั้งunstable
โมดูลเวอร์ชัน
หรือโมดูลที่ไม่มีเวอร์ชันสำหรับอินเทอร์เฟซ AIDL ที่เสถียร
ตั้งแต่ Android 12 เป็นต้นไป ชื่อโมดูลที่สร้างจากอินเทอร์เฟซ AIDL ที่เสถียรจะมีเวอร์ชันของอินเทอร์เฟซนั้นเสมอ
วิธีการอินเทอร์เฟซเมตาใหม่
Android 10 เพิ่มเมธอดอินเทอร์เฟซเมตาหลายรายการสำหรับ AIDL ที่เสถียร
ค้นหาเวอร์ชันอินเทอร์เฟซของออบเจ็กต์ระยะไกล
ไคลเอ็นต์สามารถค้นหาเวอร์ชันและแฮชของอินเทอร์เฟซที่ออบเจ็กต์ระยะไกล กำลังใช้งานอยู่ และเปรียบเทียบค่าที่แสดงผลกับค่าของอินเทอร์เฟซ ที่ไคลเอ็นต์กำลังใช้
ตัวอย่างที่ใช้cpp
แบ็กเอนด์
sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();
ตัวอย่างที่มีแบ็กเอนด์ ndk
(และ ndk_platform
)
IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);
ตัวอย่างที่ใช้java
แบ็กเอนด์
IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
// the remote side is using an older interface
}
String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();
สำหรับภาษา Java ฝั่งระยะไกลต้องใช้ getInterfaceVersion()
และ
getInterfaceHash()
ดังนี้ (ใช้ super
แทน IFoo
เพื่อหลีกเลี่ยง
ข้อผิดพลาดในการคัดลอกและวาง คุณอาจต้องใช้คำอธิบายประกอบ @SuppressWarnings("static")
เพื่อปิดใช้คำเตือน ทั้งนี้ขึ้นอยู่กับjavac
การกำหนดค่า)
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return super.VERSION; }
@Override
public final String getInterfaceHash() { return super.HASH; }
}
เนื่องจากคลาสที่สร้างขึ้น (IFoo
, IFoo.Stub
ฯลฯ) จะแชร์
ระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ (เช่น คลาสอาจอยู่ในเส้นทางคลาสของบูต) เมื่อมีการแชร์ชั้นเรียน เซิร์ฟเวอร์จะลิงก์กับชั้นเรียนเวอร์ชันล่าสุดด้วย แม้ว่าเซิร์ฟเวอร์อาจสร้างขึ้นด้วยอินเทอร์เฟซเวอร์ชันเก่ากว่า
ก็ตาม หากมีการใช้เมตาอินเทอร์เฟซนี้ในคลาสที่แชร์
ระบบจะแสดงผลเวอร์ชันล่าสุดเสมอ อย่างไรก็ตาม การใช้วิธีการ
ตามที่ระบุไว้ข้างต้นจะทำให้หมายเลขเวอร์ชันของอินเทอร์เฟซฝังอยู่ในโค้ดของเซิร์ฟเวอร์
(เนื่องจาก IFoo.VERSION
เป็น static final int
ที่ฝังเมื่อมีการอ้างอิง)
และด้วยเหตุนี้ วิธีการจึงสามารถแสดงผลเวอร์ชันที่แน่นอนซึ่งใช้สร้างเซิร์ฟเวอร์ได้
จัดการกับอินเทอร์เฟซรุ่นเก่า
ไคลเอ็นต์อาจได้รับการอัปเดตเป็นอินเทอร์เฟซ AIDL เวอร์ชันใหม่กว่า
แต่เซิร์ฟเวอร์ยังคงใช้อินเทอร์เฟซ AIDL เวอร์ชันเก่า ในกรณีเช่นนี้ การเรียกใช้เมธอดในอินเทอร์เฟซเก่าจะแสดงผลเป็น UNKNOWN_TRANSACTION
AIDL ที่เสถียรจะช่วยให้ไคลเอ็นต์ควบคุมได้มากขึ้น ในฝั่งไคลเอ็นต์ คุณสามารถตั้งค่า การติดตั้งใช้งานเริ่มต้นให้กับอินเทอร์เฟซ AIDL ได้ ระบบจะเรียกใช้เมธอดในการติดตั้งใช้งานเริ่มต้น ก็ต่อเมื่อไม่ได้ติดตั้งใช้งานเมธอดในฝั่งระยะไกล (เนื่องจากสร้างขึ้นด้วยอินเทอร์เฟซเวอร์ชันเก่ากว่า) เนื่องจากค่าเริ่มต้นได้รับการตั้งค่าทั่วโลก จึงไม่ควรใช้จากบริบทที่อาจมีการแชร์
ตัวอย่างใน C++ ใน Android 13 ขึ้นไป
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
ตัวอย่างใน Java
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
คุณไม่จำเป็นต้องระบุการใช้งานเริ่มต้นของเมธอดทั้งหมดในอินเทอร์เฟซ AIDL
เมธอดที่รับประกันว่าจะมีการใช้งานในฝั่งระยะไกล
(เนื่องจากคุณมั่นใจว่าระบบจะสร้างรีโมตเมื่อเมธอดอยู่ใน
คำอธิบายอินเทอร์เฟซ AIDL) ไม่จำเป็นต้องเขียนทับในimpl
คลาสเริ่มต้น
แปลง AIDL ที่มีอยู่เป็น AIDL แบบมีโครงสร้างหรือแบบเสถียร
หากคุณมีอินเทอร์เฟซ AIDL และโค้ดที่ใช้ AIDL อยู่แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อแปลงอินเทอร์เฟซเป็นอินเทอร์เฟซ AIDL ที่เสถียร
ระบุทรัพยากร Dependency ทั้งหมดของอินเทอร์เฟซ สำหรับทุกแพ็กเกจที่อินเทอร์เฟซขึ้นอยู่กับ ให้พิจารณาว่าแพ็กเกจนั้นกำหนดไว้ใน AIDL ที่เสถียรหรือไม่ หากไม่ได้กำหนดไว้ คุณต้องแปลงแพ็กเกจ
แปลง Parcelable ทั้งหมดในอินเทอร์เฟซเป็น Parcelable ที่เสถียร (ไฟล์อินเทอร์เฟซเองอาจยังคงเดิม) โดยทำได้ด้วยการ แสดงโครงสร้างโดยตรงในไฟล์ AIDL ต้องเขียนคลาสการจัดการใหม่เพื่อใช้ประเภทใหม่เหล่านี้ คุณทำขั้นตอนนี้ได้ก่อนที่จะสร้าง
aidl_interface
แพ็กเกจ (ด้านล่าง)สร้าง
aidl_interface
แพ็กเกจ (ตามที่อธิบายไว้ข้างต้น) ที่มี ชื่อของโมดูล การอ้างอิง และข้อมูลอื่นๆ ที่คุณต้องการ นอกจากนี้ยังต้องมีการควบคุมเวอร์ชันด้วยเพื่อให้มีความเสถียร (ไม่ใช่แค่มีโครงสร้าง) ดูข้อมูลเพิ่มเติมได้ที่การกำหนดเวอร์ชันของอินเทอร์เฟซ