###前言
程式間通訊十分重要,在Android Framework中隨處可見。
程式間通訊的時候,我們經常使用的是AIDL的方式,下面先給出一個簡單的栗子:
首先定義我們的AIDL檔案,在Android Studio建立一個AIDL檔案:
定義如下:
package com.nan.testbinder;
interface IAidlInterface {
String pay(int pwd);
}
複製程式碼
在這個AIDL裡面,我們定義一個介面,裡面有一個pay方法來模擬支付寶的支付。輸入引數是支付密碼,返回支付結果。然後我們rebuild一下專案,AS就會自動為我們定義的AIDL檔案生成一個Java檔案,這裡我們先不詳細介紹這個檔案:
然後建立我麼的遠端服務:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.nan.testbinder.IAidlInterface;
public class MyService3 extends Service {
public static final String TAG = MyService3.class.getSimpleName();
public MyService3() {
Log.e(TAG, "MyService3");
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return new Interface();
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate");
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
}
public class Interface extends IAidlInterface.Stub {
@Override
public String pay(int pwd) throws RemoteException {
if (pwd == 123) {
return "支付成功";
}
return "支付失敗";
}
}
}
複製程式碼
在這個服務裡面,我們建立了一個介面類Interface,繼承了自動生成的Java檔案裡面的Stub類。並且複寫裡面的pay方法。 最後在Service的onBind方法裡面返回這個類的物件。
我們在清單檔案中把這個服務定義為遠端服務,即指定了服務所在的程式為:remote
<service
android:name=".service.MyService3"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
複製程式碼
然後在Activity中定義一個ServiceConnection類,用於跟遠端服務連線。這裡通過Stub的asInterface方法把IBinder物件轉換為AIDL的介面。 轉換成功之後,我們就嘗試呼叫介面的pay方法,成功/失敗的時候通過一個Toast來列印支付結果。
class MyRemoteServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通過Stub的asInterface把拿到的IBinder引用轉換為AIDL介面的實現類
mRemoteInterface = IAidlInterface.Stub.asInterface(service);
if (mRemoteInterface != null) {
String result;
try {
result = mRemoteInterface.pay(123);
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//連線因為異常而終止才會呼叫
}
}
複製程式碼
最後在Activity中繫結服務/解綁服務:
mRemoteConn = new MyRemoteServiceConnection();
Intent intent4 = new Intent(MainActivity.this, MyService3.class);
//繫結
bindService(intent4, mRemoteConn, BIND_AUTO_CREATE);
//解綁
unbindService(mRemoteConn);
複製程式碼
###為什麼要使用AIDL?
我們在進行程式間通訊的時候,我們是無法拿到另外一個程式的類的物件,因為兩個程式不在一個記憶體裡面,它們都分配在不同的虛擬機器中。這樣的話,即使我們的類使用了靜態變數,編譯通過了,也無法進行通訊。
如下圖所示,兩個程式就像是一箇中國人跟一個日本人,假設雙方都不懂對方的語言。比如現在中國人要傳送一句話”不要“給日本人,但是在日語裡面”不要“就是”雅蠛蝶“,因此正常情況下是不能正常溝通的(程式直接不能直接通過變數、函式來訪問)。那麼AIDL就是我們的中間者,比如說AIDL是英語,假設中國人和日本人都懂英語,那麼兩者之間就可以間接地溝通了。
下圖是跨程式通訊的基本架構:
Android系統會單獨地分配一塊記憶體,這塊記憶體專門用於程式間的通訊的。一旦我們的遠端程式(服務)啟動以後,就會在這塊記憶體註冊一個IBinder的引用。但是這個引用跟兩個程式沒有程式上的關係,不能直接把物件扔進來,他只儲存了服務的包名、類名、這個服務有哪些變數等等。IBinder的引用的出現也就代表著客戶端可以與該服務進行通訊了。
程式間通訊是類似於C/S架構的,一個AIDL的介面包括Stub以及Proxy,分別叫做存根,代理。Stub主要跟服務端互動,Proxy主要跟客戶端互動。程式間通訊最基本的概念就是資料的讀寫,客戶端向IBinder中寫資料,Stub從IBinder讀資料。IBinder就相當於即時通訊中的中間伺服器。
###自動生成的介面類分析
package com.nan.testbinder;
public interface IAidlInterface extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.nan.testbinder.IAidlInterface {
//包名
private static final java.lang.String DESCRIPTOR = "com.nan.testbinder.IAidlInterface";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.nan.testbinder.IAidlInterface))) {
return ((com.nan.testbinder.IAidlInterface) iin);
}
return new com.nan.testbinder.IAidlInterface.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_pay: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.pay(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.nan.testbinder.IAidlInterface {
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 java.lang.String pay(int pwd) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pwd);
mRemote.transact(Stub.TRANSACTION_pay, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_pay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String pay(int pwd) throws android.os.RemoteException;
}
複製程式碼
下面開始分析,如下圖所示,這是Android Studio給出的整個類的結構圖,這個介面有兩個內部類Stub和Proxy。其中Stub是將來要從IBinder讀資料的,Proxy將要從IBinder寫資料的。
其中DESCRIPTOR是本類的包名。因為要保證兩邊能夠通訊,需要確保兩邊要知道對方的類名。 private static final java.lang.String DESCRIPTOR = "com.nan.testbinder.IAidlInterface";
下面是Stub的建構函式,裡面呼叫了父類的attachInterface函式:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
複製程式碼
這裡把this(IAidlInterface AIDL的引用,也就是自己)以及DESCRIPTOR描述繫結到Binder裡面, 如果是同一個程式之間的通訊的話,比如說Activity與同一個程式的Service通訊,那麼就會直接利用本地的介面引用。
當我們的遠端服務啟動的時候,就會註冊一個IBinder引用,一旦Service繫結成功以後就會回傳IBinder引用。但是拿到IBinder並不能馬上通訊,實際上最終還要拿到AIDL實現類的物件,這樣才能通訊。也就是說,拿到IBinder並且根據描述DESCRIPTOR來產生我們需要的AIDL的實現類。
例如上述栗子中的程式碼:
//通過Stub的asInterface把拿到的IBinder引用轉換為AIDL介面的實現類
mRemoteInterface = IAidlInterface.Stub.asInterface(service);
複製程式碼
下面我們看asInterface的原始碼,剛剛已經解釋過了,如果是同一個程式之間的通訊的話,那麼就直接使用本地的AIDL實現類引用,因此下面的程式碼是先通過IBinder的queryLocalInterface方法去找本地的引用。
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//先通過IBinder的queryLocalInterface方法去找本地的引用
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.nan.testbinder.IAidlInterface))) {
return ((com.nan.testbinder.IAidlInterface) iin);
}
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
}
複製程式碼
由於IBinder只是一個介面,那麼如果要看queryLocalInterface的實現的話,就需要去看Binder的原始碼:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
複製程式碼
這段程式碼很明確了,就是如果Stub構造的時候儲存的DESCRIPTOR與我們需要的descriptor是一致的話,說明是同一個程式的,那就直接返回本地的AIDL實現類(mOwner)。
如果本地沒有,那麼就是不同程式的,那麼這時候就需要通過IBinder引用去初始化我們的AIDL物件:
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
複製程式碼
下面就需要去看Proxy的建構函式:
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
複製程式碼
再來看一下Proxy的類,它並沒有繼承Binder,而是直接實現了AIDL的介面:
private static class Proxy implements com.nan.testbinder.IAidlInterface
複製程式碼
也就是說,Proxy構造的時候,就是通過IBinder引用轉換為AIDL的介面的實現類的過程。
下面繼續分析Stub的onTransact方法:
@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_pay: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.pay(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
複製程式碼
Stub相當於服務端,以後IBinder要向Stub進行寫資料。通訊的時候會回撥onTransact方法。
程式間通訊無非就是“函式的間接呼叫”,下面這是我在AIDL裡面定義的pay方法,如果客戶端的pay方法被呼叫,那麼遠端的相應的方法也會被呼叫。
整個過程是這樣的,先是客戶端通過Proxy向IBinder寫資料,Parcel與序列化有關。
@Override
public java.lang.String pay(int pwd) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
//把描述和資料寫到一個Parcel物件,描述用於控制將來資料傳送到哪個物件。
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pwd);
//寫資料,但是不知道是哪一個函式,只知道是第幾個函式,先去呼叫IBinder的transact
//然後IBinder去連線遠端Stub物件,最後回撥遠端Stub的onTransact方法
mRemote.transact(Stub.TRANSACTION_pay, _data, _reply, 0);
//讀取是否有異常
_reply.readException();
//讀取結果(返回值),同步讀取的
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
複製程式碼
下面我們看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;
}
複製程式碼
池的概念,先進先出的佇列的方法:
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);
}
複製程式碼
遠端Stub的onTransact方法實現:
@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_pay: {
//把DESCRIPTOR傳進來作為驗證,如果與本地的描述一樣的話,下面繼續執行。
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
//回撥自己的pay方法
java.lang.String _result = this.pay(_arg0);
//把結果返回IBinder,然後再回傳給客戶端
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
複製程式碼
###bindService的全過程原始碼分析
要看bindService的話,先要找到Context的實現類ContextImpl。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();//與系統的安全有關,如果你從系統呼叫的話,就需要關注這個方法
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
複製程式碼
######Android Studio中可以通過Ctrl+H 開啟類的繼承關係圖。
bindService呼叫了bindServiceCommon,核心程式碼:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
try {
service.prepareToLeaveProcess(this);
//核心程式碼,開始了程式間通訊
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
複製程式碼
ActivityManagerNative繼承了Binder,並且實現了IActivityManager介面,因此與程式通訊有關。
public abstract class ActivityManagerNative extends Binder implements IActivityManager{
}
複製程式碼
對比
public static abstract class Stub extends android.os.Binder implements com.nan.testbinder.IAidlInterface{
}
複製程式碼
發現ActivityManagerNative就是一個Stub物件,仔細觀察這個類就會發現下面的Proxy。與系統服務進行通訊。
繼續分析:
static public IActivityManager getDefault() {
return gDefault.get();
}
複製程式碼
拿到系統服務IBinder的引用,通過asInterface方法把IBinder引用轉換為需要的IActivityManager介面(實質上是AIDL介面):
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
//通過
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
return am;
}
};
複製程式碼
所以說,下面這句程式碼實際上是通過程式間通訊,向ServiceManager獲取名字為"activity"的系統服務,並且通過asInterface方法把IBinder引用轉換為需要的IActivityManager介面的引用,然後呼叫這個引用的bindService方法。
ActivityManagerNative.getDefault()//這就是一個ActivityManagerService(AMS)
.bindService();
複製程式碼
######系統服務,程式之間的通訊
ServiceManager--管理所有系統服務。ServiceManager相當於總檯,我們通過打電話(我們的程式與系統程式進行程式間通訊)的方式,就可以拿到對應的服務的AIDL引用。
所有系統服務啟動的時候都需要向ServiceManager註冊自己的引用:就是把自己的IBinder引用給ServiceManager。在AIDL建立Stub的時候會通過呼叫父類Binder的建構函式,通過NDK的方式去註冊。
而IActivityManager的實現類是ActivityManagerService(AMS),AMS是四大元件進行程式間通訊的一個比較重要的類。(因為他實現類AIDL介面:ActivityManagerNative)
public final class ActivityManagerService extends ActivityManagerNative implements ... {
}
複製程式碼
而AMS裡面bindService是這樣實現的,注意:現在已經是在系統服務(AMS)的程式裡面了。由系統程式繼續後續操作:
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
synchronized(this) {
//最後是通過mServices(ActiveService)物件的bindServiceLocked,這裡開始了程式間通訊
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
複製程式碼
最後是通過mServices(ActiveServices)物件的bindServiceLocked,這裡開始了程式間通訊,這個方法比較長,只看核心的程式碼:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
//判斷程式
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
//ActivityRecord用於記錄Activity,然後是為了方便處理
ActivityRecord activity = null;
if (token != null) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
int clientLabel = 0;
PendingIntent clientIntent = null;
final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;
//判斷是不是在本程式
if (isCallerSystem) {
service.setDefusable(true);
clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
if (clientIntent != null) {
clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
if (clientLabel != 0) {
service = service.cloneFilter();
}
}
}
//一些許可權的檢測
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"BIND_TREAT_LIKE_ACTIVITY");
}
//這個跟Service的生命週期有關
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
try {
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
//把我們的connection物件儲存(註冊)起來,服務繫結成功以後用於回撥
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
if (s.app != null && b.intent.received) {
try {
//連線回撥給MainActivity
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
}
//如果是第一次繫結的時候,就會呼叫這裡
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
getServiceMap(s.userId).ensureNotStartingBackground(s);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
複製程式碼
然後我們看ActiveServices中的requestServiceBindingLocked的實現,先通過forceProcessStateUpTo去啟動另外一個程式的ActivityThread的程式(即通過C++的方式反射呼叫了ActivityThread的main方法,並且啟動了訊息迴圈)。(先啟動程式,然後啟動程式裡面的服務,沒毛病!ApplicationThread的啟動代表APP的啟動,即Application的啟動)
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
//啟動ActivityThread
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//r是ServiceRecord物件,專門用來儲存Service的記錄,與ActivityRecord類似
//app是ServiceRecord的一個成員變數ProcessRecord,專門用來儲存程式的記錄
//thread是ProcessRecord的一個成員變數IApplicationThread,是整個Android元件啟動的核心類
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
} catch (RemoteException e) {
}
}
return true;
}
複製程式碼
IApplicationThread的實現類ApplicationThread是整個Android元件啟動的核心類,也是跟程式間通訊有關的類,根據下面的類的關係分析:
public interface IApplicationThread extends IInterface {
}
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread {
}
private class ApplicationThread extends ApplicationThreadNative {
}
複製程式碼
最終發現IApplicationThread的實現類是ActivityThread的一個內部類:ApplicationThread!!!
因此呼叫ApplicationThread的bindService的時候就會啟動另外一個ActivityThread,並且傳送訊息給系統Handler(H),啟動Service:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
//儲存關於Service的一些引數
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
//傳送訊息給系統Handler,啟動Service
sendMessage(H.BIND_SERVICE, s);
}
複製程式碼
系統Handler接受訊息以後:
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
複製程式碼
handleBindService:
private void handleBindService(BindServiceData data) {
//mServices是在handleCreateService的時候通過反射建立的
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
//繫結一個Service,回撥onBind方法
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
//然後又通過程式間通訊,把這個服務的IBinder物件放到Binder驅動裡面
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
複製程式碼
來到這裡,就會回撥ServiceConnection的onServiceConnected方法,拿到IBinder引用以後,就需要去建立用於通訊的AIDL介面的引用。
class MyRemoteServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteInterface = IAidlInterface.Stub.asInterface(service);
if (mRemoteInterface != null) {
String result;
try {
result = mRemoteInterface.pay(123);
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//連線因為異常而終止才會呼叫
}
}
複製程式碼
首先去初始化Stub,在Stub構造的時候,並且把描述儲存起來。
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
複製程式碼
我們bindService的時候,先會找到系統服務,由系統服務建立相應的程式和服務,然後回撥的時候傳回IBinder。 Service的建立是通過反射的方式的,當AIDL引用被建立的時候,首先會呼叫Stub的建構函式建立Stub,因為Stub繼承了Binder,根據Java的語法,這時候先會呼叫Binder的構造init方法,註冊IBinder引用到專門用於程式通訊的記憶體裡面:
public Binder() {
init();
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
}
複製程式碼
其中這裡的init方法是一個native方法:
private native final void init();
複製程式碼
在這個init方法裡面就會進行IBinder的註冊。
然後通過我們手動呼叫Stub的asInterface把拿到的IBinder引用轉換為AIDL介面的實現類:
//通過Stub的asInterface把拿到的IBinder引用轉換為AIDL介面的實現類
mRemoteInterface = IAidlInterface.Stub.asInterface(service);
複製程式碼
先在自己的程式找IBinder引用,如果有,就直接返回自身,沒有的話,就建立代理Proxy:
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//先通過IBinder的queryLocalInterface方法去找本地的引用
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.nan.testbinder.IAidlInterface))) {
return ((com.nan.testbinder.IAidlInterface) iin);
}
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
}
複製程式碼
我們在MainActivity拿到引用以後,就可以呼叫我們自己實現的pay方法進行程式間通訊了。
###資料是怎麼寫倒IBinder裡面的呢?
我們知道資料是通過Proxy寫到IBinder裡面的,例如我們剛剛看過的程式碼:
@Override
public java.lang.String pay(int pwd) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pwd);
//把資料寫到IBinder驅動裡面
mRemote.transact(Stub.TRANSACTION_pay, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
複製程式碼
這是我們自定義AIDL類裡面的自定義pay方法,這裡我們深入分析mRemote.transact(...)這個方法。 首先從上面的分析知道,mRemote是在Proxy構造的時候傳進來的(在不同程式裡的時候)。 ######這裡再強調一次,如果是同一個程式的話,Binder就是自身,因為Stub這個類本身就是繼承了Binder。
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) {
//...
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
}
複製程式碼
所以說我們要看transact的實現,實際上就要看他的實現類Binder內部類的BinderProxy的transact:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) {
Binder.getTransactionTracker().addTrace();
}
return transactNative(code, data, reply, flags);
}
複製程式碼
裡面呼叫了native方法transactNative,也就是說IBinder的底層驅動是通過C++來實現的,通過底層程式碼把資料寫到IBinder驅動裡面。
返回的時候通過Binder的onTransact函式,通過dump方法把資料拿回來:
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
//通過dump方法把資料拿回來
dump(fd.getFileDescriptor(), args);
} finally {
IoUtils.closeQuietly(fd);
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
} else if (code == SHELL_COMMAND_TRANSACTION) {
ParcelFileDescriptor in = data.readFileDescriptor();
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(err);
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
}
return true;
}
return false;
}
複製程式碼
###IBinder引用什麼時候放到共享記憶體時候?
AIDL中Stub構造的時候,就會呼叫父類Binder的構造,呼叫NDK的init方法,進行註冊。
###原始碼分析相關
原始碼分析的兩種方法:架構上面分析(類的關係)、流程上面的分析
原始碼分析的時候需要不斷去找介面的實現類 原始碼分析的推薦學習順序: Binder、Handler訊息機制、VIew、事件分發、系統服務(ServiceManager代表著系統服務、AMS代表著應用程式啟動流程)、四大元件的架構 、WindowManager、學習系統應用原始碼(Launcher等)
如果覺得我的文字對你有所幫助的話,歡迎關注我的公眾號:
我的群歡迎大家進來探討各種技術與非技術的話題,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)。