AIDL 跨程式呼叫 -- 介面層解析
基本使用方式
Service 和 Client 都需要宣告一模一樣的aidl 檔案, 然後Service 端在 onBind 的時候 將aidl 介面實現並且返回, client 端在 service connection 中 獲取並呼叫。
原理
解析AIDL 生成的檔案發現,
每次介面函式呼叫都經歷的步驟是:
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((skusBundle != null)) {
_data.writeInt(1);
skusBundle.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
- 可以發現,首先這是一個同步呼叫。
- 其次 引數和返回值的跨程式傳輸都是通過 Parcel 結構 obtain() 後的物件實現。
入參傳遞時,首先writeinterfacetoken 是一個字串,自動生成的是喚起service的action 應該可以被替換
原始碼與解釋:
/**
* Store or read an IBinder interface token in the parcel at the current
* {@link #dataPosition}. This is used to validate that the marshalled
* transaction is intended for the target interface.
*/
public final void writeInterfaceToken(String interfaceName) {
nativeWriteInterfaceToken(mNativePtr, interfaceName);
}
然後 呼叫mRemote transact ,看名字會轉發到Service 端的 Binder (那個一模一樣的aidl 介面) 的ontransact 方法中, 並且將回撥通過reply 的parcel 返回過來。 進而result 從reply 中建立起來。 每次transact 的時候會附帶對應介面方法的編號,如這裡的Stub.TRANSACTION_getSKUDetails 的int 值用於區分介面
這就是呼叫的過程
類似的,接收端也是通過 ontransact 介面的實現,根據協議約好的code解析序列化的介面
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getSkuDetails: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0 != data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
} else {
_arg3 = null;
}
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
因此傳輸和解析事實上都是通過一個Parcel 物件來完成的。
看一下這個物件的生成方式:
/**
* Container for a message (data and object references) that can
* be sent through an IBinder. A Parcel can contain both flattened data
* that will be unflattened on the other side of the IPC (using the various
* methods here for writing specific types, or the general
* {@link Parcelable} interface), and references to live {@link IBinder}
* objects that will result in the other side receiving a proxy IBinder
* connected with the original IBinder in the Parcel.
*
/**
* Retrieve a new Parcel object from the pool.
*/
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}
是從靜態物件池中獲取,如果沒有則建立一個。
最後一個問題,遠端的binder 物件我們怎麼生成轉化成這個實際的stub 物件的。 這是一個代理模式。 通過Stub中的Proxy 建立。
源頭是這個靜態方法
public static IAIDLInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IInAppBillingService))) {
return ((IInAppBillingService) iin);
}
return new IInAppBillingService.Stub.Proxy(obj);
}
而事實上,我們使用的也是這個Proxy 物件,它實現了我們AIDL中定義的介面函式,並且對每一個函式按照上面的方法序列化,然後呼叫mRemote 物件 , 也就是onService Connection 之後返回給我們的 Ibinder 物件。
綜上 IBinder 的核心 是利用Parcel 物件序列化同步呼叫傳輸的過程
而AIDL是在IBinder 的基礎上程式碼自動生成Proxy 代理類,完成序列化程式碼而簡化開發成本。
相關文章
- 使用AIDL實現跨程式介面回掉AI
- 4-AIII–Service跨程式通訊:aidlAI
- 使用C#跨PC 遠端呼叫程式並顯示UI介面C#UI
- VUE 呼叫 flask 介面,解決跨域問題VueFlask跨域
- Android 11(R) Power HAL AIDL簡析 -- 基本介面AndroidAI
- 呼叫API介面獲取淘寶商品資料:實踐指南與程式碼解析API
- 小程式呼叫本地Laravel介面 & Charles 使用Laravel
- 微信小程式之邏輯層與介面層03微信小程式
- Android程式間通訊,AIDL工作原理AndroidAI
- Android系統服務編寫例項-Binder(Java層AIDL)AndroidJavaAI
- JavaScript跨域呼叫、JSONPJavaScript跨域JSON
- webservice介面呼叫Web
- app 呼叫介面APP
- 用Promise實現小程式介面鏈式呼叫Promise
- Vector底層結構和程式碼解析
- Aidl程式間通訊詳細介紹AI
- 如何呼叫api介面API
- 一行程式碼實現Android的跨程式呼叫與通訊行程Android
- 深度解析跨域跨域
- Android AIDL原理AndroidAI
- AIDL淺析AI
- mybatis-plus原始碼解析(三)----Mapper介面動態代理呼叫過程MyBatis原始碼APP
- [初級]原生程式碼請求completions介面,完成gpt3.5 /4 介面呼叫GPT
- vue跨頁面呼叫函式Vue函式
- 騰訊WebService Api 跨域呼叫WebAPI跨域
- 從原始碼角度解析執行緒池中頂層介面和抽象類原始碼執行緒抽象
- 06.OpenFeign介面呼叫
- 前端如何取消介面呼叫前端
- Http介面呼叫示例教程HTTP
- 前端的初步----呼叫介面前端
- C++呼叫C介面C++
- RPC呼叫介面設計RPC
- 實現呼叫API介面API
- 系統呼叫篇——3環層面呼叫過程
- aidl實現halAI
- Android IPC 之AIDLAndroidAI
- [需求建議]跨模型呼叫?分類能呼叫單獨模型?模型
- 系統呼叫三層機制
- 系統呼叫篇——0環層面呼叫過程(下)