Android的IPC機制(二)——AIDL實現原理簡析
綜述
上篇說到AIDL的使用方法,我們不能僅僅只是滿足對AIDL的使用,那麼對於AIDL到底是如何實現的呢?為什麼我們只是建立一個AIDL檔案,系統就會為我們自動生成一個Java檔案,那麼這個Java檔案裡面到底包含了哪些內容呢?我們今天就來研究一下。
AIDL實現原理
在這裡我們首先看一下AIDL是怎麼實現的。當我們建立一個Service和一個AIDL介面的時候,然後建立一個Binder物件並在Service中的onBind方法中去返回這個Binder物件到客戶端,客戶端得到這個物件後就可以繫結服務端的Service,並且與服務端建立連線後就可以訪問服務端的方法了。所以在整個AIDL的實現過程中這個Binder是關鍵。那麼這個Binder究竟是什麼呢?在這裡簡要說明一下。
Binder是一種程式間的通訊方式。Binder在Linux 核心中是一個驅動程式(/dev/binder),ServiceManager通過這個Binder驅動連線各種Manager(AvtivityManager,WindowManager等)。在Android的應用層中通過Binder實現Android的RPC(Remote Procedure Call 遠端程式呼叫)過程。
以上篇文章中的加法運算為例分析來一下Binder在應用層的實現機制,我們可以看到在服務端通過new ICalculate.Stub()來建立一個Binder物件,那麼我們找到ICalculate的Java程式碼。 也就是系統根據我們的AIDL介面自動生成Java檔案。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\Android\\androidstudioProject\\aidl\\AIDLClient\\app\\src\\main\\aidl\\com\\ljd\\aidl\\ICalculate.aidl
*/
package com.ljd.aidl;
// Declare any non-default types here with import statements
public interface ICalculate extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ljd.aidl.ICalculate {
private static final java.lang.String DESCRIPTOR = "com.ljd.aidl.ICalculate";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ljd.aidl.ICalculate interface,
* generating a proxy if needed.
*/
public static com.ljd.aidl.ICalculate asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ljd.aidl.ICalculate))) {
return ((com.ljd.aidl.ICalculate)iin);
}
return new com.ljd.aidl.ICalculate.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
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;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.ljd.aidl.ICalculate {
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 int add(int first, int second) 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(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public int add(int first, int second) throws android.os.RemoteException;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
其實這段程式碼很有規律可尋的。能夠看出ICalculate 是一個繼承IInterface的介面,IInterface中只宣告瞭一個asBinder介面,用於返回Binder物件。所有在Binder傳輸的介面都必須繼承這個IInterface介面。在ICalculate的java介面中再次宣告瞭AIDL中的方法並且建立了一個繼承Binder的內部抽象類Stub。也就是說這個Stub也就是一個Binder。
在服務端我們通過new ICalculate.Stub()來建立一個Binder物件,我們來看一下這個Stub裡面的內容。Stub內定義了一個DESCRIPTOR作為Binder的唯一標識,定義了一個TRANSACTION_add 作為介面方法的id。下面看一下asInterface()方法。
public static com.ljd.binder.ICalculate asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ljd.binder.ICalculate))) {
return ((com.ljd.binder.ICalculate) iin);
}
return new com.ljd.binder.ICalculate.Stub.Proxy(obj);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
這個方法是在客戶端被呼叫,執行在主執行緒當中,是將服務端返回來的Binder物件轉為客戶端所需要的介面物件。通過Binder物件的queryLocalInterface方法去查詢客戶端程式中是否存在所需要的介面物件。存在的話就直接返回這個介面物件。也就是說這時候客戶端和服務端在同一個程式內。若是不存在,就建立一個Proxy物件。Proxy是Stub的一個內部代理類。我們看一下Proxy裡面到底做了什麼。
private static class Proxy implements com.ljd.aidl.ICalculate {
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 int add(int first, int second) 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(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
在跨程式通訊中,由於是在asInterface方法中建立了一個Proxy物件,所以,Proxy也是執行在客戶端的主執行緒中。在Proxy類中實現了ICalculate介面,定義了IBinder介面物件。通過實現asBinder介面返回這個IBinder介面物件。而對於我們自己定義的add介面方法中建立兩個Parcel物件_data和_reply (Parcel可以說是一個存放讀取資料的容器,在Parcel內部包裝了可序列化的資料,並且可以在Binder中自由的傳輸)。我們可以很清晰看出_data物件寫入了我們傳入的引數,而_reply 則是用來存放方法的返回值。緊接著呼叫了Binder的transact方法。在transact方法方法中將方法id,_data,_reply作為引數傳入進去。下面看一下Binder的transact方法。
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
我們可以很明顯的看到在transact方法中呼叫了onTransact方法並返回了一個boolean型別。當返回false的時候客戶端請求就會失敗。在Stub中我們重寫onTransact方法。下面我們看一下onTransact方法。
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_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;
}
}
return super.onTransact(code, data, reply, flags);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
在onTransact方法中我們會根據code來判斷所需要執行的方法,通過data取出所需要的引數,然後執行該方法,若是有返回值則將將返回值寫入reply中。在這裡Parcel物件data和reply讀寫順序要嚴格保持一致。
可以看出Binder執行的整個流程也就是:當客戶端繫結服務端發起遠端請求,客戶端程式處於休眠,當前執行緒處於掛起狀態。然後服務端開始執行,執行完畢後將結果返回給客戶端。然後客戶端繼續執行。
這也說明了當遠端方法是一個很耗時的操作時,我們不應該在主執行緒中發起請求。而服務端的Binder方法在Binder執行緒池中執行,所以Binder不論是否耗時都不應該重新為他在開啟一個執行緒。
到這裡AIDL中Binder的工作機制已經分析完了。現在我們可以發現完全不用AIDL檔案也能夠實現跨程式的方法呼叫。那麼我們自己來寫一個Binder去實現服務端與客戶端的跨程式的方法呼叫。
自定義Binder的實現
實現過程
在這裡我們建立一個繼承IInterfaceJava介面,在介面裡面我們去宣告算術的加減法運算。
package com.ljd.binder.custombinder;
import android.os.IInterface;
public interface ICalculate extends IInterface {
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public int add(int first, int second) throws android.os.RemoteException;
public int sub(int first, int second) throws android.os.RemoteException;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
然後我們再去實現這個介面。
package com.ljd.binder.custombinder;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
/**
* Created by ljd-PC on 2016/2/21.
*/
public class Calculate extends Binder implements ICalculate{
private final String TAG = "Calculate";
private static final String DESCRIPTOR = "com.ljd.binder.ICalculate";
@Override
public void attachInterface(IInterface owner, String descriptor) {
this.attachInterface(this, DESCRIPTOR);
}
public static ICalculate asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof ICalculate))) {
return (ICalculate) iin;
}
return new Proxy(obj);
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
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;
}
case TRANSACTION_sub: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sub(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public int add(int first, int second) throws RemoteException {
Log.e(TAG,String.valueOf(first+second));
return first + second;
}
@Override
public int sub(int first, int second) throws RemoteException {
return first - second;
}
@Override
public IBinder asBinder() {
return null;
}
private static class Proxy implements ICalculate {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int add(int first, int second) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(first);
_data.writeInt(second);
mRemote.transact(TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int sub(int first, int second) throws 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(first);
_data.writeInt(second);
mRemote.transact(TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
在這裡建立一個Service。
package com.ljd.binder.custombinder;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class CalculateService extends Service {
private Calculate mCalculate;
public CalculateService() {
mCalculate = new Calculate();
}
@Override
public IBinder onBind(Intent intent) {
return mCalculate;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在上篇文章中說過我們可以通過android:process 屬性為服務端開啟一個程式。
<service
android:name=".custombinder.CalculateService"
android:process=":remote">
<intent-filter>
<action android:name="com.ljd.binder.CUSTOM_BINDER"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</service>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其中:remote是一種簡寫法,全稱為com.ljd.binder.custombinder:remote。以:開頭的程式是屬於當前應用的私有程式,其他應用的元件不能和他執行在同一個程式中。
下面是客戶端程式碼。
package com.ljd.binder.custombinder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.ljd.binder.*;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class CustomBinderActivity extends AppCompatActivity {
private final String TAG = "CustomBinderActivity";
private ICalculate mCalculate;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if(mCalculate != null){
mCalculate.asBinder().unlinkToDeath(mDeathRecipient,0);
mCalculate = null;
bindService();
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"Bind Success");
mCalculate = Calculate.asInterface(service);
try {
mCalculate.asBinder().linkToDeath(mDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mCalculate = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_binder);
ButterKnife.bind(this);
bindService();
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
unbindService(mConnection);
}
@OnClick({R.id.custom_add_btn,R.id.custom_sub_btn})
public void onClickButton(View v){
if (mCalculate == null){
return;
}
switch (v.getId()){
case R.id.custom_add_btn:
try {
Toast.makeText(this,String.valueOf(mCalculate.add(6,2)),Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.custom_sub_btn:
try {
Toast.makeText(this,String.valueOf(mCalculate.sub(6,2)),Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
private void bindService(){
Intent intent = new Intent("com.ljd.binder.CUSTOM_BINDER");
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
效果演示
總結
我們現在自己已經實現了跨程式間的呼叫。而且我們構建的這個Binder幾乎和系統生成的一模一樣。所以AIDL就是對Binder進行了一次封裝,並且能夠支援多執行緒併發訪問。通過AIDL的使用能夠大大簡化了我們開發過程,節約了我們的開發時間。
相關文章
- Android IPC 之AIDLAndroidAI
- 藉助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析AIAndroid
- Android IPC 機制分析Android
- Android AIDL原理AndroidAI
- Android RollBack機制實現原理剖析Android
- Android 11(R) Power HAL AIDL簡析 -- 基本介面AndroidAI
- Android IPC機制(三):淺談Binder的使用Android
- Android-Handler訊息機制實現原理Android
- 02.Android之IPC機制問題Android
- 詳解 Android 中的 IPC 機制:基礎篇Android
- NX實現機制淺析
- Binder機制之AIDLAI
- Android Handler與Looper原理簡析AndroidOOP
- AIDL淺析AI
- Android程式間通訊,AIDL工作原理AndroidAI
- 藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈AIAndroid
- 簡析Windows訊息機制Windows
- Android 流暢度檢測原理簡析Android
- Android中mmap原理及應用簡析Android
- aidl實現halAI
- 微前端調研及簡析SPA實現原理前端
- 利用反射機制實現依賴注入的原理反射依賴注入
- Nestjs模組機制的概念和實現原理JS
- javascript事件機制底層實現原理JavaScript事件
- Android應用加固的簡單實現方案(二)Android
- Entitas實現簡析
- Android10_原理機制系列_事件傳遞機制Android事件
- Android探索之旅 | AIDL原理和例項講解AndroidAI
- 深入探究immutable.js的實現機制(二)JS
- 學習筆記(2)IPC機制筆記
- 基於Netty的Android系統IM簡單實現原理NettyAndroid
- HashMap原理(二) 擴容機制及存取原理HashMap
- 微信域名檢測的機制原理以及實現方式
- 深入理解Java的垃圾回收機制(GC)實現原理JavaGC
- Angular 依賴注入機制實現原理的深入介紹Angular依賴注入
- Android Handler MessageQueue Looper 訊息機制原理AndroidOOP
- Android 原始碼分析(二)handler 機制Android原始碼
- Kubernetes List-Watch 機制原理與實現 - chunked
- ObjC中Category的原理簡析OBJGo