摘要:本節主要來講解Android10.0 AIDL的通信原理
閱讀本文大約需要花費24分鐘。
文章首發微信公衆號:IngresGe
專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!
[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析
[Android取經之路] 系列文章:
《系統啓動篇》
- Android系統架構
- Android是怎麼啓動的
- Android 10.0系統啓動之init進程
- Android10.0系統啓動之Zygote進程
- Android 10.0 系統啓動之SystemServer進程
- Android 10.0 系統服務之ActivityMnagerService
- Android10.0系統啓動之Launcher(桌面)啓動流程
- Android10.0應用進程創建過程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及啓動流程
- Android 10.0 PackageManagerService(二)權限掃描
- Android 10.0 PackageManagerService(三)APK掃描
- Android 10.0 PackageManagerService(四)APK安裝流程
《日誌系統篇》
- Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
- Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
- Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
- Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現
《Binder通信原理》:
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入門篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- Android10.0 Binder通信原理(四)-Native-C\C++實例分析
- Android10.0 Binder通信原理(五)-Binder驅動分析
- Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
- Android10.0 Binder通信原理(七)-Framework binder示例
- Android10.0 Binder通信原理(八)-Framework層分析
- Android10.0 Binder通信原理(九)-AIDL Binder示例
- Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式
- Android10.0 Binder通信原理(十一)-Binder總結
1.概述
上一節我們寫了一個AIDL的示例,實現了兩個應用之間的通信,這一節我們就來一起探討下AIDL是如何生效的。
2.什麼是AIDL
AIDL:Android Interface Definition Language,即Android接口定義語言。
Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。爲了使其他的應用程序也可以訪問本應用程序提供的服務,Android系統採用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。我們知道4個Android應用程序組件中的3個(Activity、BroadcastReceiver和ContentProvider)都可以進行跨進程訪問,另外一個Android應用程序組件Service同樣可以。因此,可以將這種可以跨進程訪問的服務稱爲AIDL(Android Interface Definition Language)服務。
3.爲什麼要用AIDL
Android中每個應用都是獨立的進程,擁有自己的虛擬機,虛擬地址,應用之間的內存不止不能互相訪問,存在應用隔離,因此兩個應用不能像面嚮對象語言一樣直接進行接口的調用。兩個進程之間的調用叫做IPC(進程間通信)。在Binder的起始章節,我們瞭解到Android中進程之間的IPC調用有:管道、共享內存、消息隊列、信號量、socket、binder,在《Binder入門篇》中,從性能、安全角度分別講解了各個IPC通信的優缺點,最終我們選擇了Binder。
那麼既然我們有了Binder,爲什麼還要有AIDL呢?
在我們前面的 《Framrwork binder示例》 中,我們知道,通過binder來進行client\server時,我們寫了完成的服務創建和client獲取流程,在上一節AIDL示例中,我們寫完AIDL 編譯後,發現生成的IMyService.java文件就和我們在Framework中寫的類似,AIDL簡化了Binder的代碼邏輯,把跟Service交互的邏輯通過工具編譯來生成。
4.AIDL通信流程
Client 端和Server端使用同一個AIDL,連包名都需要保持一致。
Server端繼承自Service,重載一個onBind(),返回服務實體Stub(),Stub提供了一個asInterface(Binder)的方法,如果是在同一個進程下那麼asInterface()將返回Stub對象本身,否則返回Stub.Proxy對象.
Code:
IMyService.Stub mStub = new IMyService.Stub(){...};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
return mStub;//通過ServiceConnection在activity中拿到MyService
}
Client 綁定服務時通過拿到服務Stub.asInterface返回的服務的代理Stub.Proxy()
Code:
myService = IMyService.Stub.asInterface(service);
Client和Server交互的簡單示意流程:
從上面的示例來看,服務本地拿到了AIDL生成的服務實體Stub(), Client綁定服務後,拿到了服務的代理Stub.proxy()。這和我們在前面Framewok層講解的比較類似了,Client拿到BinderProxy對象,Server拿到Binder實體對象。
AIDL在這裏用到了一個Proxy-Stub (代理-存根)的設計模式,下面我們就這種設計模式來展開說明一下。
Binder通信的數據流轉如下圖所示:
5.proxy-stub 設計模式
Proxy將特殊性接口轉換成通用性接口,Stub將通用性接口轉換成特殊性接口,二者之間的數據轉換通過Parcel(打包)進行的,Proxy常作爲數據發送代理,通過Parcel將數據打包發送,Stub常作爲數據接收樁,解包並解析Parcel Data package。
舉例理解Proxy-Stub:
假如我們現在要看電視,我是客戶Client,遙控器是代理Proxy,電視機是實體(播放畫面,展示功能),遙控器傳給電視機的藍牙、紅外參數爲Parcel數據。
我按下了遙控器的一些按鍵-提升音量,遙控器之前跟電視機做了綁定,可以拿到電視機的對象--實體Stub,把按鍵的操作組裝成一個Parcel數據,發給電視機-Server,電視機-Server拿到請求後,執行相應的處理-提升音量,結果返回給遙控器,我們操作完成(這一步其實沒有,我們只是假想一下)。這樣就完成了Proxy-Stub的數據交互流程。
Proxy和Stub的說明:
- Stub 跟 Proxy 是一對,俗稱“代理-存根”,一般用在遠程方法調用
- Proxy 的接口供客戶端程序調用,然後它內部會把信息包裝好,以某種方式傳遞給 Stub,而後者通過對應的接口作用於服務端系統,從而完成了“遠程調用”
- AIDL中,Stub爲服務實體;Stub.Proxy()爲服務的代理,都是通過Stub.asInterface(IBinder)中獲取,可以通過AIDL生成的java文件看出。
public static com.android.myservice.IMyService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.myservice.IMyService))) {
return ((com.android.myservice.IMyService)iin); //如果是同一進程,返回的是服務Stub本身
}
return new com.android.myservice.IMyService.Stub.Proxy(obj); //如果是不同進程,則返回Stub.Proxy()代理
}
6.AIDL原理分析
在上一節,IMyService.aidl編譯後,Android Studio自動生成了IMyService.java文件,我們來看看這個文件的內容:
Code:IMyService.java
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.android.myservice;
// Declare any non-default types here with import statements
public interface IMyService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.myservice.IMyService
{
private static final java.lang.String DESCRIPTOR = "com.android.myservice.IMyService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.myservice.IMyService interface,
* generating a proxy if needed.
*/
public static com.android.myservice.IMyService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.myservice.IMyService))) {
return ((com.android.myservice.IMyService)iin);
}
return new com.android.myservice.IMyService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.android.myservice.IMyService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int add(int num1, int num2) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
IMyService.java 的說明:
- 有一個Stub的抽象類,Stub中又有一個Proxy的抽象類
- Stub.asInterface(IBinder) 會根據是同一進行通信,還是不同進程通信,返回Stub()實體,或者Stub.Proxy()代理對象
- Stub()的Binder實體中有個onTransact()函數,在前面的一些Binder Native、Framework的流程,我們知道,服務最終處理的入口就是onTransact(),這裏會解析Client傳來的 TRANSACTION code ,解析Parcel數據,調用對應的服務接口處理
- Proxy()中存在一個asBinder(),返回的對象爲mRemote,就如我們前面Framework瞭解的,對應的其實是BinderProxy對象。
- Proxy()組裝了Client中的AIDL接口的核心實現,組裝Parcel數據,調用BinderProxy()的transact()發送TRANSACTION code
AIDL的具體流程如下:
- Client和Server都使用同一個AIDL文件,包名相同,編譯後,兩邊都會生成IMyService.java,其中有Stub實體和Proxy代理兩個對象
- Server端通過AndroidManifest.xml 註冊Service
- Client通過bindService()獲得服務的代理Stub.Proxy()
- Client 調用AIDL的方法add(),其實調用的是IMyService.java中的Stub.Proxy.add(),最終通過BinderProxy.java的transact()向服務端發送
- 通過Binder驅動的流程,進入到服務端的onTransact(),根據Client發送的TRANSACTION code,解析進入相應的流程處理,進入add()
- MyService在被綁定時,有了實體IMyService.Stub,最終進入MyService.java的add()處理,完成接口調用,調用完成後把數據寫入Parcel,通過reply發送給Client
7.AIDL的配置方法
AIDL 使用一種簡單語法,允許您通過一個或多個方法(可接收參數和返回值)來聲明接口。參數和返回值可爲任意類型,甚至是 AIDL 生成的其他接口。
您必須使用 Java 編程語言構建 .aidl 文件。每個 .aidl 文件均須定義單個接口,並且只需要接口聲明和方法簽名。
默認情況下,AIDL 支持下列數據類型:
-
Java 編程語言中的所有原語類型(如 int、long、char、boolean 等)
-
String
-
CharSequence
-
List
List 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。您可選擇將 List 用作“泛型”類(例如,List<String>)。儘管生成的方法旨在使用 List 接口,但另一方實際接收的具體類始終是 ArrayList。
-
Map
Map 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。儘管生成的方法旨在使用 Map 接口,但另一方實際接收的具體類始終是 HashMap。
定義服務接口時,請注意:
方法可帶零個或多個參數,返回值或空值。
所有非原語參數均需要指示數據走向的方向標記。這類標記可以是 in、out 或 inout(見下方示例)。
原語默認爲 in,不能是其他方向。
oneway 關鍵字用於修改遠程調用的行爲。使用此關鍵字後,遠程調用不會屏蔽,而只是發送事務數據並立即返回。最終接收該數據時,接口的實現會將其視爲來自 Binder 線程池的常規調用(普通的遠程調用)。如果 oneway 用於本地調用,則不會有任何影響,且調用仍爲同步調用。
8.總結
Client 通過Proxy向Server進行請求,最終進入Binder Driver,binder根據不同的事務處理,發送給Binder實體,實體中根據不同的TRANSACTION code轉入不同的邏輯處理,處理完得到結果後,會把數據組裝爲Parcel,通過reply發送出來,Client收到reply的數據,進行最終流程處理。
參考:
《Android Binder 完全解析(三)AIDL實現原理分析》
我的微信公衆號:IngresGe