Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.

AIDL ổn định

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Android 10 bổ sung hỗ trợ cho Ngôn ngữ Định nghĩa Giao diện Android (AIDL) ổn định, một cách mới để theo dõi giao diện chương trình ứng dụng (API) / giao diện nhị phân ứng dụng (ABI) được cung cấp bởi các giao diện AIDL. AIDL ổn định có những điểm khác biệt chính sau đây so với AIDL:

  • Các giao diện được định nghĩa trong hệ thống xây dựng với aidl_interfaces .
  • Giao diện chỉ có thể chứa dữ liệu có cấu trúc. Các phần tử đại diện cho các loại mong muốn được tạo tự động dựa trên định nghĩa AIDL của chúng và được sắp xếp tự động và không được quản lý.
  • Các giao diện có thể được khai báo là ổn định (tương thích ngược). Khi điều này xảy ra, API của họ được theo dõi và tạo phiên bản trong một tệp bên cạnh giao diện AIDL.

Xác định giao diện AIDL

Định nghĩa về aidl_interface trông như thế này:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions: ["1", "2"],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
    },

}
  • name : Tên của mô-đun giao diện AIDL nhận dạng duy nhất một giao diện AIDL.
  • srcs : Danh sách các tệp nguồn AIDL tạo giao diện. Đường dẫn cho loại AIDL mà Foo xác định trong gói com.acme phải ở <base_path>/com/acme/Foo.aidl , trong đó <base_path> có thể là bất kỳ thư mục nào liên quan đến thư mục chứa Android.bp . Trong ví dụ trên, <base_path>srcs/aidl .
  • local_include_dir : Đường dẫn từ nơi bắt đầu tên gói. Nó tương ứng với <base_path> giải thích ở trên.
  • imports : Một danh sách các mô-đun aidl_interface mà điều này sử dụng. Nếu một trong các giao diện AIDL của bạn sử dụng một giao diện hoặc một giao diện có thể mua được từ một aidl_interface khác, hãy đặt tên của nó ở đây. Đây có thể là tên của chính nó, để chỉ phiên bản mới nhất hoặc tên có hậu tố phiên bản (chẳng hạn như -V1 ) để chỉ một phiên bản cụ thể. Chỉ định một phiên bản đã được hỗ trợ kể từ Android 12
  • versions : Các phiên bản trước của giao diện bị đóng băng trong api_dir , Bắt đầu từ Android 11, các versions bị đóng băng dưới aidl_api/ name . Nếu không có phiên bản giao diện cố định nào, thì điều này sẽ không được chỉ định và sẽ không có kiểm tra tính tương thích.
  • stability : Cờ tùy chọn cho lời hứa ổn định của giao diện này. Hiện tại chỉ hỗ trợ "vintf" . Nếu điều này không được đặt, điều này tương ứng với một giao diện có tính ổn định trong ngữ cảnh biên dịch này (vì vậy một giao diện được tải ở đây chỉ có thể được sử dụng với những thứ được biên dịch cùng nhau, ví dụ như trên system.img). Nếu điều này được đặt thành "vintf" , điều này tương ứng với một lời hứa ổn định: giao diện phải được giữ ổn định miễn là nó được sử dụng.
  • gen_trace : Cờ tùy chọn để bật hoặc tắt tính năng theo dõi. Mặc định là false .
  • host_supported : Cờ tùy chọn khi được đặt thành true sẽ làm cho các thư viện được tạo sẵn có cho môi trường máy chủ.
  • unstable : Cờ tùy chọn được sử dụng để đánh dấu rằng giao diện này không cần phải ổn định. Khi điều này được đặt thành true , hệ thống xây dựng sẽ không tạo kết xuất API cho giao diện cũng như không yêu cầu nó được cập nhật.
  • backend.<type>.enabled : Các cờ này chuyển đổi từng backend mà trình biên dịch AIDL sẽ tạo mã. Hiện tại, ba phần mềm phụ trợ được hỗ trợ: java , cppndk . Các phần phụ trợ đều được bật theo mặc định. Khi một chương trình phụ trợ cụ thể không cần thiết, nó cần được vô hiệu hóa một cách rõ ràng.
  • backend.<type>.apex_available : Danh sách các tên APEX mà thư viện sơ khai đã tạo sẵn có.
  • backend.[cpp|java].gen_log : Cờ tùy chọn kiểm soát việc tạo mã bổ sung để thu thập thông tin về giao dịch.
  • backend.[cpp|java].vndk.enabled : Cờ tùy chọn để biến giao diện này trở thành một phần của VNDK. Mặc định là false .
  • backend.java.platform_apis : Cờ tùy chọn kiểm soát liệu thư viện sơ khai Java có được xây dựng dựa trên các API riêng từ nền tảng hay không. Điều này phải được đặt thành "true" khi stability được đặt thành "vintf" .
  • backend.java.sdk_version : Cờ tùy chọn để chỉ định phiên bản SDK mà thư viện sơ khai Java được xây dựng dựa trên. Mặc định là "system_current" . Điều này không nên được đặt khi backend.java.platform_apis là true.
  • backend.java.platform_apis : Cờ tùy chọn phải được đặt thành true khi các thư viện được tạo cần xây dựng dựa trên API nền tảng hơn là SDK.

Đối với mỗi sự kết hợp của các versions và phần phụ trợ được kích hoạt, một thư viện sơ khai sẽ được tạo. Xem Quy tắc đặt tên mô-đun để biết cách tham chiếu đến phiên bản cụ thể của thư viện sơ khai cho một chương trình phụ trợ cụ thể.

Viết tệp AIDL

Các giao diện trong AIDL ổn định tương tự như các giao diện truyền thống, ngoại trừ việc chúng không được phép sử dụng các gói không có cấu trúc (vì chúng không ổn định!). Sự khác biệt cơ bản trong AIDL ổn định là cách các gói được xác định. Trước đây, các bưu kiện đã được khai báo trước ; trong AIDL ổn định, các trường thửa và biến được xác định rõ ràng.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Mặc định hiện được hỗ trợ (nhưng không bắt buộc) cho boolean , char , float , double , byte , int , longString . Trong Android 12, các giá trị mặc định cho kiểu liệt kê do người dùng xác định cũng được hỗ trợ. Khi giá trị mặc định không được chỉ định, giá trị giống như 0 hoặc giá trị trống sẽ được sử dụng. Các bảng kê không có giá trị mặc định được khởi tạo bằng 0 ngay cả khi không có bảng kê nào.

Sử dụng thư viện sơ khai

Sau khi thêm thư viện sơ khai làm phụ thuộc vào mô-đun của bạn, bạn có thể đưa chúng vào tệp của mình. Dưới đây là các ví dụ về thư viện sơ khai trong hệ thống xây dựng ( Android.mk cũng có thể được sử dụng cho các định nghĩa mô-đun kế thừa):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}

Ví dụ trong C ++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Ví dụ trong Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Giao diện tạo phiên bản

Khai báo một mô-đun với tên foo cũng tạo một mục tiêu trong hệ thống xây dựng mà bạn có thể sử dụng để quản lý API của mô-đun. Khi được tạo, foo-freeze-api thêm định nghĩa API mới dưới tên api_dir hoặc aidl_api/ name , tùy thuộc vào phiên bản Android và thêm tệp .hash , cả hai đều đại diện cho phiên bản mới được đóng băng của giao diện. Việc xây dựng này cũng cập nhật thuộc tính versions để phản ánh phiên bản bổ sung. Sau khi thuộc tính versions được chỉ định, hệ thống xây dựng sẽ chạy kiểm tra tính tương thích giữa các phiên bản đã được đóng băng và cũng như giữa Top of Tree (ToT) và phiên bản được đóng băng mới nhất.

Ngoài ra, bạn cần quản lý định nghĩa API của phiên bản ToT. Bất cứ khi nào một API được cập nhật, hãy chạy foo-update-api để cập nhật aidl_api/ name /current chứa định nghĩa API của phiên bản ToT.

Để duy trì sự ổn định của giao diện, chủ sở hữu có thể thêm mới:

  • Các phương thức ở cuối giao diện (hoặc các phương thức với các phần nối tiếp mới được xác định rõ ràng)
  • Các phần tử ở cuối một phân lô (yêu cầu một phần tử mặc định được thêm vào cho mỗi phần tử)
  • Giá trị không đổi
  • Trong Android 11, điều tra viên
  • Trong Android 12, các trường ở cuối liên hợp

Không có hành động nào khác được phép và không ai khác có thể sửa đổi giao diện (nếu không, họ có nguy cơ va chạm với các thay đổi mà chủ sở hữu thực hiện).

Để kiểm tra xem tất cả các giao diện đã được đóng băng để phát hành hay chưa, bạn có thể xây dựng với bộ biến môi trường sau:

  • AIDL_FROZEN_REL=true m ... - bản dựng yêu cầu tất cả các giao diện AIDL ổn định phải được đóng băng mà không có trường owner: được chỉ định.
  • AIDL_FROZEN_OWNERS="aosp test" - xây dựng yêu cầu tất cả các giao diện AIDL ổn định được đóng băng với trường owner: được chỉ định là "aosp" hoặc "test".

Sử dụng các giao diện được tạo phiên bản

Phương thức giao diện

Trong thời gian chạy, khi cố gắng gọi các phương thức mới trên máy chủ cũ, các máy khách mới gặp lỗi hoặc ngoại lệ, tùy thuộc vào phần phụ trợ.

  • cpp backend được ::android::UNKNOWN_TRANSACTION .
  • Chương trình phụ trợ ndk nhận được STATUS_UNKNOWN_TRANSACTION .
  • java backend nhận android.os.RemoteException với thông báo cho biết API không được triển khai.

Để biết các chiến lược xử lý điều này, hãy xem các phiên bản truy vấnsử dụng các giá trị mặc định .

Bưu kiện

Khi các trường mới được thêm vào bưu kiện, các máy khách và máy chủ cũ sẽ loại bỏ chúng. Khi máy khách và máy chủ mới nhận được các ô cũ, các giá trị mặc định cho các trường mới sẽ tự động được điền vào. Điều này có nghĩa là các giá trị mặc định cần được chỉ định cho tất cả các trường mới trong một ô.

Khách hàng không nên mong đợi máy chủ sử dụng các trường mới trừ khi họ biết máy chủ đang triển khai phiên bản đã xác định trường (xem các phiên bản truy vấn ).

Enums và hằng số

Tương tự như vậy, các máy khách và máy chủ nên từ chối hoặc bỏ qua các giá trị không đổi không được công nhận và các bảng liệt kê nếu thích hợp, vì nhiều hơn có thể được thêm vào trong tương lai. Ví dụ, một máy chủ không nên hủy bỏ khi nó nhận được một điều tra viên mà nó không biết về. Nó nên bỏ qua nó hoặc trả lại một cái gì đó để khách hàng biết nó không được hỗ trợ trong quá trình triển khai này.

Đoàn thể

Cố gắng gửi kết hợp với trường mới không thành công nếu người nhận đã cũ và không biết về trường. Việc triển khai sẽ không bao giờ thấy sự hợp nhất với lĩnh vực mới. Sự thất bại được bỏ qua nếu đó là một giao dịch một chiều; nếu không thì lỗi là BAD_VALUE (đối với phụ trợ C ++ hoặc NDK) hoặc IllegalArgumentException (đối với phụ trợ Java). Lỗi được nhận nếu máy khách đang gửi một tập hợp liên hợp cho trường mới đến một máy chủ cũ hoặc khi một máy khách cũ nhận liên kết từ một máy chủ mới.

Quy tắc đặt tên mô-đun

Trong Android 11, đối với mỗi sự kết hợp của các phiên bản và phần phụ trợ được bật, mô-đun thư viện sơ khai sẽ tự động được tạo. Để tham chiếu đến một mô-đun thư viện sơ khai cụ thể để liên kết, không sử dụng tên của mô-đun aidl_interface , mà hãy sử dụng tên của mô-đun thư viện sơ khai, là ifacename - version - backend , trong đó

  • ifacename : tên của mô-đun aidl_interface
  • version là một trong hai
    • V version-number cho các phiên bản đông lạnh
    • V latest-frozen-version-number + 1 cho phiên bản ngọn cây (chưa-đóng-băng)
  • backend là một trong hai
    • java cho chương trình phụ trợ Java,
    • cpp cho phần phụ trợ C ++,
    • ndk hoặc ndk_platform cho chương trình phụ trợ NDK. Cái trước là dành cho ứng dụng và cái sau dành cho việc sử dụng nền tảng.

Giả sử rằng có một mô-đun có tên foo và phiên bản mới nhất của nó là 2 và nó hỗ trợ cả NDK và C ++. Trong trường hợp này, AIDL tạo các mô-đun sau:

  • Dựa trên phiên bản 1
    • foo-V1-(java|cpp|ndk|ndk_platform)
  • Dựa trên phiên bản 2 (phiên bản ổn định mới nhất)
    • foo-V2-(java|cpp|ndk|ndk_platform)
  • Dựa trên phiên bản ToT
    • foo-V3-(java|cpp|ndk|ndk_platform)

So với Android 11,

  • foo- backend , được gọi là phiên bản ổn định mới nhất trở thành foo- V2 - backend
  • foo-unstable- backend , được gọi là phiên bản ToT trở thành foo- V3 - backend

Tên tệp đầu ra luôn giống với tên mô-đun.

  • Dựa trên phiên bản 1: foo-V1-(cpp|ndk|ndk_platform).so
  • Dựa trên phiên bản 2: foo-V2-(cpp|ndk|ndk_platform).so
  • Dựa trên phiên bản ToT: foo-V3-(cpp|ndk|ndk_platform).so

Lưu ý rằng trình biên dịch AIDL không tạo mô-đun phiên bản unstable hoặc mô-đun không có phiên bản cho giao diện AIDL ổn định. Kể từ Android 12, tên mô-đun được tạo từ giao diện AIDL ổn định luôn bao gồm phiên bản của nó.

Các phương pháp giao diện meta mới

Android 10 bổ sung một số phương thức giao diện meta cho AIDL ổn định.

Truy vấn phiên bản giao diện của đối tượng từ xa

Khách hàng có thể truy vấn phiên bản và mã băm của giao diện mà đối tượng từ xa đang triển khai và so sánh các giá trị trả về với các giá trị của giao diện mà khách hàng đang sử dụng.

Ví dụ với chương trình phụ trợ 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();

Ví dụ với phụ trợ ndk (và 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);

Ví dụ với chương trình phụ trợ 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();

Đối với ngôn ngữ Java, phía từ xa PHẢI triển khai getInterfaceVersion()getInterfaceHash() như sau:

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return IFoo.VERSION; }

    @Override
    public final String getInterfaceHash() { return IFoo.HASH; }
}

Điều này là do các lớp được tạo ( IFoo , IFoo.Stub , v.v.) được chia sẻ giữa máy khách và máy chủ (ví dụ: các lớp có thể nằm trong đường dẫn khởi động). Khi các lớp được chia sẻ, máy chủ cũng được liên kết với phiên bản mới nhất của các lớp mặc dù nó có thể đã được xây dựng với phiên bản cũ hơn của giao diện. Nếu giao diện meta này được triển khai trong lớp chia sẻ, nó luôn trả về phiên bản mới nhất. Tuy nhiên, bằng cách triển khai phương pháp như trên, số phiên bản của giao diện được nhúng vào mã của máy chủ (vì IFoo.VERSION là một static final int được nội tuyến khi được tham chiếu) và do đó phương pháp có thể trả về phiên bản chính xác mà máy chủ đã được xây dựng với.

Xử lý các giao diện cũ hơn

Có thể máy khách được cập nhật phiên bản mới hơn của giao diện AIDL nhưng máy chủ đang sử dụng giao diện AIDL cũ. Trong những trường hợp như vậy, việc gọi một phương thức trên giao diện cũ sẽ trả về UNKNOWN_TRANSACTION .

Với AIDL ổn định, khách hàng có nhiều quyền kiểm soát hơn. Ở phía máy khách, bạn có thể đặt triển khai mặc định thành giao diện AIDL. Một phương thức trong triển khai mặc định chỉ được gọi khi phương thức đó không được triển khai ở phía từ xa (vì nó được xây dựng với phiên bản cũ hơn của giao diện). Vì các giá trị mặc định được đặt trên toàn cầu, chúng không nên được sử dụng từ các ngữ cảnh có thể được chia sẻ.

Ví dụ trong C ++ trong Android 13 trở lên:

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

Ví dụ trong 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(...);

Bạn không cần cung cấp triển khai mặc định của tất cả các phương pháp trong giao diện AIDL. Các phương thức được đảm bảo thực hiện ở phía điều khiển từ xa (vì bạn chắc chắn rằng điều khiển từ xa được xây dựng khi các phương thức nằm trong mô tả giao diện AIDL) không cần phải được ghi đè trong lớp impl mặc định.

Chuyển đổi AIDL hiện có thành AIDL có cấu trúc / ổn định

Nếu bạn có giao diện AIDL hiện có và mã sử dụng nó, hãy sử dụng các bước sau để chuyển đổi giao diện sang giao diện AIDL ổn định.

  1. Xác định tất cả các yếu tố phụ thuộc vào giao diện của bạn. Đối với mỗi gói mà giao diện phụ thuộc vào, hãy xác định xem gói đó có được xác định trong AIDL ổn định hay không. Nếu không được xác định, gói phải được chuyển đổi.

  2. Chuyển đổi tất cả các gói trong giao diện của bạn thành các gói ổn định (bản thân các tệp giao diện có thể không thay đổi). Làm điều này bằng cách thể hiện cấu trúc của chúng trực tiếp trong các tệp AIDL. Các lớp quản lý phải được viết lại để sử dụng các kiểu mới này. Điều này có thể được thực hiện trước khi bạn tạo một gói aidl_interface (bên dưới).

  3. Tạo một gói aidl_interface (như được mô tả ở trên) chứa tên mô-đun của bạn, các phụ thuộc của nó và bất kỳ thông tin nào khác mà bạn cần. Để làm cho nó ổn định (không chỉ có cấu trúc), nó cũng cần được tạo phiên bản. Để biết thêm thông tin, hãy xem Giao diện phiên bản .