概述
aidl是常用的android IPC方式,本文將根據一個demo來決議下AIDL的原理,
為了便于讀者理解,本文不會探究Binder的實作細節,可以認為Binder在此文的分析中被看做是一個“黑盒”,
有一定經驗的讀者可以直接到文末看總結,最終流程圖如下:

Demo
demo實作內容:
MainActivity通過AIDL實作IPC來呼叫另一個行程中RemoteTestService的一個方法,
主要有以下幾個檔案:
- MainActivity.java
自定義ServiceConnection,然后將ServiceConnection傳入bindService,獲取到IBinder后實作遠程呼叫, - RemoteTestService.java
在ITestServer.Stub中實作需要遠程呼叫的方法testFunction(),在onBind中回傳, - IServer.aidl
定義一個aidl檔案和需要遠程呼叫的方法 - AndroidManifest.xml
在此設定RemoteTestService在remote行程,
MainActivity.java
public class MainActivity extends AppCompatActivity {
private IServer iServer;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iServer = IServer.Stub.asInterface(service);
System.out.println("onServiceConnected");
try {
int a = iServer.testFunction("test string");
Log.i("test", "after testFunction a:" + a);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, RemoteTestService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
RemoteTestService.java
public class RemoteTestService extends Service {
private IServer.Stub serverBinder = new IServer.Stub() {
@Override public int testFunction(String s) throws RemoteException {
Log.i("test","testFunction s= " + s);
return 0;
}
};
@Nullable @Override
public IBinder onBind(Intent intent) {
return serverBinder;
}
}
IServer.aidl
interface IServer {
int testFunction(String s);
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bindservicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".RemoteTestService"
android:process=":remote" />
</application>
</manifest>
aidl自動生成的檔案
定義完aidl檔案,編譯會自動生成一個java介面檔案,
這個介面檔案在build目錄下,具體路徑如下:

打開檔案,我們就可以看到aidl自動生成的代碼,
public interface IServer extends android.os.IInterface
{
/** Default implementation for IServer. */
public static class Default implements com.example.bindservicetest.IServer
{
@Override public int testFunction(java.lang.String s) throws android.os.RemoteException
{
return 0;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.bindservicetest.IServer
{
private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.IServer";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.bindservicetest.IServer interface,
* generating a proxy if needed.
*/
public static com.example.bindservicetest.IServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.bindservicetest.IServer))) {
return ((com.example.bindservicetest.IServer)iin);
}
return new com.example.bindservicetest.IServer.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_testFunction:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.testFunction(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.bindservicetest.IServer
{
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;
}
@Override public int testFunction(java.lang.String s) 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.writeString(s);
boolean _status = mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().testFunction(s);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.bindservicetest.IServer sDefaultImpl;
}
static final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.bindservicetest.IServer impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.bindservicetest.IServer getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public int testFunction(java.lang.String s) throws android.os.RemoteException;
}
根據原始碼的簡潔版來決議
aidl檔案本身并不會參與代碼的運行,編譯后自動生成的檔案才是關鍵,因此這個檔案由開發者來手動實作也是完全可以的,
自動生成的介面檔案,有較多代碼在demo的IPC中并沒有使用,
因此筆者打算用原始碼決議就用自己寫的介面檔案,也是減少給讀者的混淆項,
筆者自己實作的 介面ITestServer.java,原始碼如下:
public interface ITestServer extends android.os.IInterface {
public int testFunction(java.lang.String s) throws android.os.RemoteException;
public static abstract class Stub extends android.os.Binder
implements com.example.bindservicetest.ITestServer {
//binder唯一標識
private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.ITestServer";
//testFunction的呼叫id
static final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public Stub() {
//將interface提供出去,這樣當統一行程其他位置執行IBinder.queryLocalInterface的時候就可以獲取到這個Binder
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.bindservicetest.ITestServer asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//查找當前行程是否有這個binder
if (((iin != null) && (iin instanceof com.example.bindservicetest.IServer))) {
return ((com.example.bindservicetest.ITestServer) iin);
}
return new com.example.bindservicetest.ITestServer.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder() {
return this;//繼承自IInterface,回傳當前Stub的binder物件
}
@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_testFunction: {
data.enforceInterface(descriptor);//需要傳遞DESCRIPTOR,到另一個行程可以找到對應的Binder
java.lang.String _arg0;
_arg0 = data.readString();//讀取引數
int _result = this.testFunction(_arg0);//運行
reply.writeNoException();
reply.writeInt(_result);//寫入回傳值
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.bindservicetest.ITestServer {
private final android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override public android.os.IBinder asBinder() {
return mRemote;//繼承自IInterface,回傳當前Proxy的binder物件
}
@Override public int testFunction(java.lang.String s) 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);//需要傳遞DESCRIPTOR,到另一個行程可以找到對應的Binder
_data.writeString(s);//寫入引數
boolean _status =
mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通過transact來IPC呼叫
_reply.readException();
_result = _reply.readInt();//讀取運行結果
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
}
ITestServer 原始碼決議
通過android studio,可以看到該類的結構如下,

接下來我們就主要分析三個類:
- ITestServer
Stub和Proxy都implement了它,
java上層可以使用這個介面來保留Binder的物件,在使用aidl的時候,完全可以只使用這個物件而不用關心這個物件是Stub還是Proxy, - ITestServer.Stub
當前行程的Binder物件,
在onTransact方法中實作了對IPC方法的邏輯,Proxy呼叫的時候會觸發,
如果是相同行程則是直接呼叫,不會觸發onTransact方法, - ITestServer.Stub.Proxy
當前行程通過Proxy實作了對遠端方法的呼叫,
當前行程呼叫遠端行程方法,實際上是呼叫了遠端Binder的transact方法,最終執行到Stub中的onTransact,
ITestServer
public interface ITestServer extends android.os.IInterface {
public int testFunction(java.lang.String s) throws android.os.RemoteException;
----------------Stub的代碼先不看
}
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
要點如下:
- ITestServer本身只包括“需要IPC的方法”,此處即int testFunction(String)
- 這個類繼承了IInterface,需要實作asBinder介面,
asBinder()會回傳正確的Binder物件,如果是同行程呼叫就會直接回傳當前行程的Binder,如果是IPC,就會回傳遠程呼叫的行程的Binder,
對應原始碼就是,Stub的asBinder()回傳的是this,而Proxy回傳的是mRemote.
ITestServer.Stub
DESCRIPTOR
//binder唯一標識
private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.ITestServer";
是Binder的唯一標識,
不同的行程之間,通過序列化傳遞DESCRIPTOR來找到對應的Binder,
相同行程,也需要通過DESCRIPTOR才找到對應的Binder,
TRANSACTION_testFunction
//testFunction的呼叫id
static final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
呼叫IPC方法的唯一id,
Stub.onTransact()中會通過TRANSACTION_testFunction來對應 執行testFunction()方法的邏輯,
Proxy呼叫transact的時候,也是通過傳遞TRANSACTION_testFunction,來標識自己想要執行的邏輯,
Stub初始化
public Stub() {
//將interface提供出去,這樣當統一行程其他位置執行IBinder.queryLocalInterface的時候就可以獲取到這個Binder
this.attachInterface(this, DESCRIPTOR);
}
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
* to return the given owner IInterface when the corresponding
* descriptor is requested.
*/
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
初始化時,呼叫attachInterface(),
attachInterface()之后,相同行程呼叫queryLocalInterface()的時候就能找到這個Binder,
在下面asInterface()中,就用到了queryLocalInterface(),
asInterface()
public static com.example.bindservicetest.ITestServer asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//查找當前行程是否有這個binder
if (((iin != null) && (iin instanceof com.example.bindservicetest.IServer))) {
return ((com.example.bindservicetest.ITestServer) iin);
}
return new com.example.bindservicetest.ITestServer.Stub.Proxy(obj);
}
用于將服務端的IBinder物件轉化成ITestServer物件,
如果是server和client行程相同,那么會直接通過queryLocalInterface(DESCRIPTOR)找到Binder回傳,這里回傳的其實就是Stub物件,
如果server和client在不同行程,那么會回傳一個封裝后的Proxy物件,
asBinder()
@Override public android.os.IBinder asBinder() {
return this;//繼承自IInterface,回傳當前Stub的binder物件
}
回傳Stub自己,
在Proxy中回傳的是遠端行程的Binder,
onTransact()
@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_testFunction: {
data.enforceInterface(descriptor);//需要傳遞DESCRIPTOR,到另一個行程可以找到對應的Binder
java.lang.String _arg0;
_arg0 = data.readString();//讀取引數
int _result = this.testFunction(_arg0);//運行
reply.writeNoException();
reply.writeInt(_result);//寫入回傳值
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
實作了被IPC呼叫后的邏輯,
一個方法的呼叫在onTransact中一般有以下幾個邏輯:
- 從data中讀出引數
- 將讀出的引數帶入server的方法中,得到執行結果,
- 將執行結果寫入reply
引數和結果為什么讀寫都需要通過Parcel?
行程間通信由于資源不共享,因此無法直接傳遞物件,只能通過序列化在不同的空間拷貝兩份相同的資源,
而Parcel就是序列化的一種方案,
ITestServer.Stub.Proxy
private static class Proxy implements com.example.bindservicetest.ITestServer {
private final android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override public android.os.IBinder asBinder() {
return mRemote;//繼承自IInterface,回傳當前Proxy的binder物件
}
@Override public int testFunction(java.lang.String s) 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);//需要傳遞DESCRIPTOR,到另一個行程可以找到對應的Binder
_data.writeString(s);//寫入引數
boolean _status =
mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通過transact來IPC呼叫
_reply.readException();
_result = _reply.readInt();//讀取運行結果
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
Proxy初始化
private final android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
Proxy在初始化的時候會參考server的IBinder,
asBinder()
@Override public android.os.IBinder asBinder() {
return mRemote;//繼承自IInterface,回傳當前Proxy的binder物件
}
Proxy在asBinder的時候會回傳server的IBinder,
Stub在這個方法中會回傳this,
testFunction()
@Override public int testFunction(java.lang.String s) 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);//需要傳遞DESCRIPTOR,到另一個行程可以找到對應的Binder
_data.writeString(s);//寫入引數
boolean _status =
mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通過transact來IPC呼叫
_reply.readException();
_result = _reply.readInt();//讀取運行結果
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
Proxy呼叫testFunction,實際上是通過呼叫mRemote.transact()來觸發遠端Stub的onTransact(),
一般呼叫的步驟如下:
- 創建引數與回傳值的Parcel物件,將引數寫入Parcel,
- 呼叫mRemote.transact(),回傳值會寫入到Parcel物件中,
- 從Parcel物件中讀出回傳值并return,
總結
根據上文的分析,aidl自動生成的檔案的原始碼大概可以總結成下圖:

基本步驟如下:
- Client通過ServiceConnection獲取到Server的Binder,并且封裝成一個Proxy,
- 通過Proxy來同步呼叫IPC方法,同時通過Parcel將引數傳給Binder,最終觸發Binder的transact方法,
- Binder的transact方法最侄訓觸發到Server上Stub的onTransact方法,
- Server上Stub的onTransact方法中,會先從Parcel中決議中引數,然后將引數帶入真正的方法中執行,然后將結果寫入Parcel后傳回,
- Client的Ipc方法中,執行Binder的transact時,是阻塞等待的,一直到Server邏輯執行結束后才會繼續執行,
- 當Server回傳結果后,Client從Parcel中取出回傳值,于是實作了一次IPC呼叫,
繼續探索
讀完本文,細心的讀者會發現本文還留有兩個較大的問題完全沒有提及:
- ServiceConnection是如何獲取到Binder的
- 通過ServiceConnection獲取到的Binder是什么,或者說java層的Binder是什么
這兩點筆者后面的文章會繼續探索,有興趣的讀者可以自行探索或者歡迎持續關注,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/226887.html
標籤:其他
上一篇:AMD CPU 的電腦運行 Android Studio 模擬器時報錯 Failed to install Intel HAXM
