AIDL เวอร์ชันเสถียร

Android 10 เพิ่มการรองรับ Android Interface Definition Language (AIDL) เวอร์ชันเสถียร ซึ่งเป็นวิธีใหม่ในการตรวจสอบ Application Programming Interface (API) และ Application Binary Interface (ABI) ที่อินเทอร์เฟซ AIDL ระบุ AIDL เวอร์ชันเสถียรทํางานเหมือนกับ AIDL ทุกประการ แต่ระบบบิลด์จะติดตามความเข้ากันได้ของอินเทอร์เฟซ และมีข้อจํากัดในสิ่งที่คุณทําได้ ดังนี้

  • อินเทอร์เฟซจะกำหนดไว้ในระบบบิลด์ด้วย aidl_interfaces
  • อินเทอร์เฟซมีได้เฉพาะ Structured Data เท่านั้น ระบบจะสร้าง Parcelable ที่แสดงถึงประเภทที่ต้องการโดยอัตโนมัติตามคำจำกัดความ AIDL และจะทำการจัดเรียงและการจัดเรียงใหม่โดยอัตโนมัติ
  • อินเทอร์เฟซสามารถประกาศว่าเสถียร (เข้ากันได้แบบย้อนหลัง) เมื่อเกิดกรณีนี้ ระบบจะติดตามและกำหนดเวอร์ชัน API ไว้ในไฟล์ข้างอินเทอร์เฟซ AIDL

Structured AIDL กับ AIDL เวอร์ชันเสถียร

Structured AIDL หมายถึงประเภทที่กําหนดไว้ใน AIDL เท่านั้น เช่น การประกาศที่แยกย่อยได้ (การแยกย่อยที่กําหนดเอง) ไม่ใช่ AIDL ที่มีโครงสร้าง Parcelable ที่มีการกำหนดช่องใน AIDL จะเรียกว่า Structured Parcelable

AIDL ที่เสถียรต้องใช้ AIDL ที่มีโครงสร้างเพื่อให้ระบบการบิลด์และคอมไพเลอร์เข้าใจว่าการเปลี่ยนแปลงที่ทํากับ Parcelable นั้นเข้ากันได้แบบย้อนหลังหรือไม่ อย่างไรก็ตาม อินเทอร์เฟซที่มีโครงสร้างบางรายการอาจไม่เสถียร อินเทอร์เฟซต้องใช้เฉพาะ Structured Type และต้องใช้ฟีเจอร์การจัดรุ่นต่อไปนี้ด้วยจึงจะทำงานได้อย่างเสถียร ในทางกลับกัน อินเทอร์เฟซจะไม่เสถียรหากใช้ระบบบิลด์หลักเพื่อสร้างหรือหากตั้งค่า 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 ที่ประกอบเป็นอินเทอร์เฟซ เส้นทางสำหรับ AIDL ประเภท Foo ที่กําหนดไว้ในแพ็กเกจ 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 12
  • versions: อินเทอร์เฟซเวอร์ชันเก่าที่แช่แข็งไว้ใต้ api_dir โดยตั้งแต่ Android 11 เป็นต้นไป versions จะแช่แข็งไว้ใต้ aidl_api/name หากอินเทอร์เฟซไม่มีเวอร์ชันที่หยุดพัฒนาแล้ว คุณไม่ควรระบุค่านี้และจะไม่มีการตรวจสอบความเข้ากันได้ ช่องนี้มีการแทนที่ด้วย versions_with_info สำหรับ Android 13 ขึ้นไป
  • versions_with_info: รายการทูเพลตแต่ละรายการมีชื่อของเวอร์ชันที่หยุดทำงานและรายการที่มีเวอร์ชันการนําเข้าของโมดูล aidl_interface อื่นๆ ที่ aidl_interface เวอร์ชันนี้นําเข้า คําจํากัดความของเวอร์ชัน V ของอินเทอร์เฟซ AIDL IFACE อยู่ที่ aidl_api/IFACE/V ช่องนี้เปิดตัวใน Android 13 และไม่ควรแก้ไขใน Android.bp โดยตรง ระบบจะเพิ่มหรืออัปเดตช่องโดยการเรียกใช้ *-update-api หรือ *-freeze-api นอกจากนี้ ระบบจะย้ายข้อมูลช่อง versions ไปยัง versions_with_info โดยอัตโนมัติเมื่อผู้ใช้เรียกใช้ *-update-api หรือ *-freeze-api
  • stability: Flag ไม่บังคับสำหรับสัญญาความเสถียรของอินเทอร์เฟซนี้ การดำเนินการนี้รองรับเฉพาะ "vintf" หากไม่ได้ตั้งค่า stability ระบบจะตรวจสอบว่าอินเทอร์เฟซเข้ากันได้แบบย้อนหลัง เว้นแต่จะมีการระบุ unstable การตั้งค่าเป็น "ไม่ตั้งค่า" สอดคล้องกับอินเทอร์เฟซที่มีความเสถียรภายในบริบทการคอมไพล์นี้ (ดังนั้น สิ่งต่างๆ ทั้งหมดของระบบ เช่น สิ่งต่างๆ ใน system.img และพาร์ติชันที่เกี่ยวข้อง หรือสิ่งต่างๆ ทั้งหมดของผู้ให้บริการ เช่น สิ่งต่างๆ ใน vendor.img และพาร์ติชันที่เกี่ยวข้อง) หากตั้งค่า stability เป็น "vintf" แสดงว่าสอดคล้องกับสัญญาความเสถียร ซึ่งหมายความว่าอินเทอร์เฟซต้องมีความเสถียรตราบใดที่มีการใช้งาน
  • gen_trace: แฟล็กที่ไม่บังคับสำหรับเปิดหรือปิดการติดตาม โดยตั้งแต่ Android 14 เป็นต้นไป ค่าเริ่มต้นคือ true สำหรับแบ็กเอนด์ cpp และ java
  • host_supported: Flag ไม่บังคับซึ่งเมื่อตั้งค่าเป็น true จะทำให้ไลบรารีที่สร้างขึ้นพร้อมใช้งานสำหรับสภาพแวดล้อมโฮสต์
  • unstable: Flag ที่ไม่บังคับซึ่งใช้ทำเครื่องหมายว่าอินเทอร์เฟซนี้ไม่จำเป็นต้องเสถียร เมื่อตั้งค่าเป็น true ระบบบิลด์จะไม่สร้างการดัมพ์ API สําหรับอินเทอร์เฟซหรือกําหนดให้อัปเดต
  • frozen: Flag ไม่บังคับซึ่งเมื่อตั้งค่าเป็น true หมายความว่าอินเทอร์เฟซไม่มีการเปลี่ยนแปลงนับตั้งแต่อินเทอร์เฟซเวอร์ชันก่อนหน้า ซึ่งจะเปิดใช้การตรวจสอบเพิ่มเติมในยามที่สร้าง เมื่อตั้งค่าเป็น false หมายความว่าอินเทอร์เฟซอยู่ระหว่างการพัฒนาและมีการเปลี่ยนแปลงใหม่ ดังนั้นการเรียกใช้ foo-freeze-api จะสร้างเวอร์ชันใหม่และเปลี่ยนค่าเป็น true โดยอัตโนมัติ เปิดตัวใน Android 14
  • backend.<type>.enabled: Flag เหล่านี้จะเปิด/ปิดแบ็กเอนด์แต่ละรายการที่คอมไพเลอร์ AIDL สร้างขึ้น ระบบรองรับแบ็กเอนด์ 4 รายการ ได้แก่ Java, C++, NDK และ Rust ระบบจะเปิดใช้แบ็กเอนด์ Java, C++ และ NDK โดยค่าเริ่มต้น หากไม่จำเป็นต้องใช้แบ็กเอนด์ใดเลย จะต้องปิดใช้แบ็กเอนด์นั้นอย่างชัดเจน ระบบจะปิดใช้ Rust โดยค่าเริ่มต้นจนกว่า Android 15 จะพร้อมให้บริการ
  • backend.<type>.apex_available: รายการชื่อ APEX ที่ไลบรารีสแต็บที่สร้างขึ้นใช้ได้
  • backend.[cpp|java].gen_log: Flag ไม่บังคับที่ใช้ควบคุมว่าจะสร้างโค้ดเพิ่มเติมเพื่อรวบรวมข้อมูลเกี่ยวกับธุรกรรมหรือไม่
  • backend.[cpp|java].vndk.enabled: Flag ที่ไม่บังคับเพื่อทำให้อินเทอร์เฟซนี้เป็นส่วนหนึ่งของ VNDK ค่าเริ่มต้นคือ false
  • backend.[cpp|ndk].additional_shared_libraries: เปิดตัวใน Android 14 โดย Flag นี้จะเพิ่มการพึ่งพาไปยังไลบรารีแบบเนทีฟ ธงนี้มีประโยชน์กับ ndk_header และ cpp_header
  • backend.java.sdk_version: Flag ไม่บังคับสำหรับการระบุเวอร์ชันของ SDK ที่ใช้ในการสร้างไลบรารีสแต็บ Java โดยมีค่าเริ่มต้นเป็น "system_current" ไม่ควรตั้งค่านี้เมื่อ backend.java.platform_apis เป็น true
  • backend.java.platform_apis: Flag ที่ไม่บังคับซึ่งควรตั้งค่าเป็น true เมื่อต้องสร้างไลบรารีที่สร้างขึ้นจาก API ของแพลตฟอร์มแทน SDK

ระบบจะสร้างไลบรารีสแต็บสําหรับชุดค่าผสมของเวอร์ชันและแบ็กเอนด์ที่เปิดใช้แต่ละชุด ดูวิธีอ้างอิงไลบรารีสแต็บเวอร์ชันที่เจาะจงสําหรับแบ็กเอนด์ที่เจาะจงได้ที่กฎการตั้งชื่อโมดูล

เขียนไฟล์ AIDL

อินเทอร์เฟซใน AIDL ที่เสถียรจะคล้ายกับอินเทอร์เฟซแบบดั้งเดิม ยกเว้นว่าไม่อนุญาตให้ใช้ Parcelable ที่ไม่มีโครงสร้าง (เนื่องจากไม่เสถียร โปรดดูAIDL แบบมีโครงสร้างเทียบกับแบบเสถียร) ความแตกต่างหลักใน AIDL เวอร์ชันเสถียรคือวิธีกำหนดรายการที่แบ่งได้ ก่อนหน้านี้ Parcelable มีการประกาศล่วงหน้า แต่ใน AIDL เวอร์ชันเสถียร (และ Structured) ฟิลด์และตัวแปร 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 ก็ตาม

ใช้ไลบรารีสแต็บ

หลังจากเพิ่มไลบรารีสแต็บเป็นข้อกำหนดของโมดูลแล้ว คุณจะรวมไลบรารีเหล่านั้นไว้ในไฟล์ได้ ต่อไปนี้คือตัวอย่างไลบรารีสแต็บในระบบบิลด์ (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 แล้ว ระบบบิลด์จะเรียกใช้การตรวจสอบความเข้ากันได้ระหว่างเวอร์ชันที่หยุดทำงานและระหว่างเวอร์ชันยอดนิยม (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" - บิลด์กำหนดให้อินเทอร์เฟซ AIDL ที่เสถียรทั้งหมดต้องถูกหยุดไว้ชั่วคราวโดยระบุช่อง owner: เป็น "aosp" หรือ "test"

ความเสถียรของการนําเข้า

การอัปเดตเวอร์ชันของการนําเข้าสําหรับอินเทอร์เฟซเวอร์ชันที่หยุดไว้จะใช้งานร่วมกับเลเยอร์ AIDL ที่เสถียรได้ อย่างไรก็ตาม การอัปเดตสิ่งเหล่านี้ต้องอัปเดตเซิร์ฟเวอร์และไคลเอ็นต์ทั้งหมดที่ใช้อินเทอร์เฟซเวอร์ชันเก่า และบางแอปอาจสับสนเมื่อมีการผสมผสานประเภทต่างๆ เวอร์ชันต่างๆ โดยทั่วไปแล้ว แพ็กเกจประเภทเดียวหรือแพ็กเกจทั่วไปจะปลอดภัยเนื่องจากต้องมีการเขียนโค้ดเพื่อจัดการประเภทที่ไม่รู้จักจากธุรกรรม IPC อยู่แล้ว

ในโค้ดแพลตฟอร์ม Android android.hardware.graphics.common เป็นตัวอย่างการอัปเกรดเวอร์ชันประเภทนี้ที่ใหญ่ที่สุด

ใช้อินเทอร์เฟซที่มีเวอร์ชัน

วิธีการของอินเทอร์เฟซ

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

  • cpp backend ได้รับ ::android::UNKNOWN_TRANSACTION
  • ndk backend ได้รับ STATUS_UNKNOWN_TRANSACTION
  • แบ็กเอนด์ java ได้รับ android.os.RemoteException พร้อมข้อความว่าไม่ได้ติดตั้งใช้งาน API

ดูกลยุทธ์ในการจัดการปัญหานี้ได้ที่หัวข้อการค้นหาเวอร์ชันและการใช้ค่าเริ่มต้น

พาร์เซล

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

ลูกค้าไม่ควรคาดหวังว่าเซิร์ฟเวอร์จะใช้ช่องใหม่ เว้นแต่ว่าลูกค้าจะทราบว่าเซิร์ฟเวอร์กำลังใช้งานเวอร์ชันที่มีการกําหนดช่อง (ดูการค้นหาเวอร์ชัน)

Enum และค่าคงที่

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

สหภาพแรงงาน

การพยายามส่งยูเนียนที่มีช่องใหม่จะไม่สำเร็จหากผู้รับเป็นเวอร์ชันเก่าและไม่รู้จักช่องดังกล่าว การใช้งานจะไม่เห็นการรวมช่องใหม่ ระบบจะไม่สนใจความล้มเหลวหากเป็นธุรกรรมแบบทางเดียว มิฉะนั้นข้อผิดพลาดคือ BAD_VALUE(สําหรับแบ็กเอนด์ C++ หรือ NDK) หรือ IllegalArgumentException(สําหรับแบ็กเอนด์ Java) ระบบจะแสดงข้อผิดพลาดหากไคลเอ็นต์ส่งชุดยูเนียนไปยังช่องใหม่ไปยังเซิร์ฟเวอร์เก่า หรือเมื่อเป็นไคลเอ็นต์เก่าที่รับยูเนียนจากเซิร์ฟเวอร์ใหม่

จัดการหลายเวอร์ชัน

เนมสเปซของ linker ใน Android อาจมีอินเทอร์เฟซ aidl ที่เฉพาะเจาะจงได้เพียง 1 เวอร์ชันเท่านั้น เพื่อหลีกเลี่ยงกรณีที่ประเภท aidl ที่สร้างขึ้นมีคำจำกัดความหลายรายการ C++ มีกฎคำจำกัดความเดียวที่กำหนดให้มีคำจำกัดความของสัญลักษณ์แต่ละรายการเพียงรายการเดียว

บิลด์ Android จะแสดงข้อผิดพลาดเมื่อโมดูลใช้aidl_interfaceคลังเดียวกันในเวอร์ชันที่แตกต่างกัน โมดูลอาจขึ้นต่อกันกับไลบรารีเหล่านี้โดยตรงหรือโดยอ้อมผ่านทรัพยากร 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_interfaces_defaults สามารถใช้เพื่อเก็บคำจำกัดความของข้อกำหนดเวอร์ชันล่าสุดสำหรับ aidl_interface ไว้ 1 รายการ ซึ่งอัปเดตได้ในที่เดียว และโมดูล 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 รองรับการสำรองรันไทม์สำหรับไลบรารีอินเทอร์เฟซที่เลิกตรึงเหล่านี้เพื่อให้เขียนโค้ดกับเวอร์ชันล่าสุดที่เลิกตรึงและยังคงใช้งานได้ในอุปกรณ์รุ่นที่เผยแพร่ ลักษณะการทำงานที่เข้ากันได้แบบย้อนหลังของไคลเอ็นต์จะคล้ายกับลักษณะการทำงานที่มีอยู่ และการใช้ฟีเจอร์สำรองยังต้องเป็นไปตามลักษณะการทำงานเหล่านั้นด้วย ดูหัวข้อใช้อินเทอร์เฟซเวอร์ชัน

Flag การสร้าง AIDL

Flag ที่ควบคุมลักษณะการทำงานนี้มีRELEASE_AIDL_USE_UNFROZEN การกําหนดไว้ใน build/release/build_flags.bzl true หมายความว่าอินเทอร์เฟซเวอร์ชันที่เลิกแช่แข็งมีการใช้งานขณะรันไทม์ และ false หมายความว่าไลบรารีของเวอร์ชันที่เลิกแช่แข็งทั้งหมดจะทำงานเหมือนเวอร์ชันที่แช่แข็งล่าสุด คุณสามารถลบล้าง Flag เป็น true สำหรับการพัฒนาการในเครื่องได้ แต่ต้องเปลี่ยนกลับไปเป็น false ก่อนเผยแพร่ โดยปกติแล้วการพัฒนาจะทําด้วยการกำหนดค่าที่ตั้งค่า Flag เป็น true

เมทริกซ์และไฟล์ Manifest ของความเข้ากันได้

ออบเจ็กต์อินเทอร์เฟซของผู้ให้บริการ (ออบเจ็กต์ VINTF) จะกำหนดเวอร์ชันที่คาดไว้และเวอร์ชันที่ระบุไว้ในแต่ละฝั่งของอินเทอร์เฟซของผู้ให้บริการ

อุปกรณ์ที่ไม่ใช่ 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 เครื่องมือจัดการบริการจะไม่อนุญาตให้บริการลงทะเบียนอินเทอร์เฟซ และไคลเอ็นต์จะไม่พบอินเทอร์เฟซดังกล่าว

คุณสามารถเพิ่มบริการแบบมีเงื่อนไขตามค่าของ Flag RELEASE_AIDL_USE_UNFROZEN ในไฟล์ Make ของอุปกรณ์ได้โดยทำดังนี้

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

หากบริการเป็นส่วนหนึ่งของกระบวนการที่ใหญ่กว่าซึ่งคุณไม่สามารถเพิ่มลงในอุปกรณ์แบบมีเงื่อนไขได้ ให้ตรวจสอบว่ามีการประกาศบริการด้วย IServiceManager::isDeclared() หรือไม่ หากมีการประกาศและลงทะเบียนไม่สำเร็จ ให้ยกเลิกกระบวนการ หากไม่ได้ประกาศ คาดว่าการลงทะเบียนจะไม่สำเร็จ

Cuttlefish เป็นเครื่องมือสำหรับการพัฒนา

ทุกปีหลังจากที่ VINTF หยุดทำงาน เราจะปรับตารางความเข้ากันได้ของเฟรมเวิร์ก (FCM) target-level และ PRODUCT_SHIPPING_API_LEVEL ของ Cuttlefish เพื่อให้สอดคล้องกับอุปกรณ์ที่เปิดตัวพร้อมกับรุ่นของปีถัดไป เราปรับ target-level และ PRODUCT_SHIPPING_API_LEVEL เพื่อให้แน่ใจว่าอุปกรณ์ที่จะเปิดตัวบางรุ่นได้รับการทดสอบและเป็นไปตามข้อกำหนดใหม่สำหรับการเปิดตัวในปีหน้า

เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น true ระบบจะใช้ Cuttlefish เพื่อพัฒนา Android เวอร์ชันในอนาคต แอปกำหนดเป้าหมายเป็น FCM ระดับ PRODUCT_SHIPPING_API_LEVEL ของ Android รุ่นปีหน้า โดยต้องเป็นไปตามข้อกำหนดซอฟต์แวร์ของผู้ให้บริการ (VSR) ของรุ่นถัดไป

เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false ปลาหมึกจะมี target-level และ PRODUCT_SHIPPING_API_LEVEL ก่อนหน้าเพื่อแสดงอุปกรณ์รุ่น ใน Android 14 และต่ำกว่า การแยกความแตกต่างนี้จะทำได้โดยใช้สาขา Git ที่แตกต่างกันซึ่งไม่รับการเปลี่ยนแปลง FCMtarget-level, ระดับ API ที่ใช้งานจริง หรือโค้ดอื่นๆ ที่กําหนดเป้าหมายเป็นรุ่นถัดไป

กฎการตั้งชื่อโมดูล

ใน Android 11 ระบบจะสร้างโมดูลไลบรารีสแต็บโดยอัตโนมัติสําหรับชุดค่าผสมของเวอร์ชันและแบ็กเอนด์ที่เปิดใช้แต่ละชุด หากต้องการอ้างอิงถึงโมดูลไลบรารีสแต็บที่เฉพาะเจาะจงสำหรับการลิงก์ อย่าใช้ชื่อของโมดูล aidl_interface แต่ให้ใช้ชื่อของโมดูลไลบรารีสแต็บ ซึ่งก็คือ ifacename-version-backend โดยที่

  • ifacename: ชื่อโมดูล aidl_interface
  • version เป็นค่าใดค่าหนึ่งต่อไปนี้
    • Vversion-number สำหรับเวอร์ชันที่อัปเดตไม่ได้
    • Vlatest-frozen-version-number + 1 สำหรับเวอร์ชันยอดนิยม (ยังไม่ได้หยุดอัปเดต)
  • backend เป็นค่าใดค่าหนึ่งต่อไปนี้
    • java สำหรับแบ็กเอนด์ Java
    • cpp สำหรับแบ็กเอนด์ 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 เวอร์ชันจะกลายเป็น 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 ที่เสถียร

  1. ระบุทรัพยากร Dependency ทั้งหมดของอินเทอร์เฟซ สําหรับทุกแพ็กเกจที่อินเทอร์เฟซนั้นใช้อยู่ ให้ตรวจสอบว่าแพ็กเกจได้รับการกําหนดไว้ใน AIDL เวอร์ชันเสถียรหรือไม่ หากไม่ได้กําหนดไว้ จะต้องแปลงแพ็กเกจ

  2. แปลง Parcelable ทั้งหมดในอินเทอร์เฟซเป็น Parcelable ที่เสถียร (ไฟล์อินเทอร์เฟซจะยังคงเหมือนเดิมได้) โดยระบุโครงสร้างในไฟล์ AIDL โดยตรง คลาสการจัดการต้องเขียนใหม่เพื่อใช้ประเภทใหม่เหล่านี้ ซึ่งทำได้ก่อนสร้างแพ็กเกจ aidl_interface (ด้านล่าง)

  3. สร้างแพ็กเกจ aidl_interface (ตามที่อธิบายไว้ด้านบน) ที่มีชื่อของโมดูล ข้อมูลที่ต้องพึ่งพา และข้อมูลอื่นๆ ที่ต้องการ เนื้อหาต้องได้รับการจัดทําเวอร์ชันด้วยเพื่อให้มีเสถียรภาพ (ไม่ใช่แค่มีโครงสร้าง) ดูข้อมูลเพิ่มเติมได้ที่การกำหนดเวอร์ชันอินเทอร์เฟซ