Android 原始碼分析之旅2 1 IPC以及Service的啟動過程

小楠總發表於2017-12-21

###前言

程式間通訊十分重要,在Android Framework中隨處可見。

程式間通訊的時候,我們經常使用的是AIDL的方式,下面先給出一個簡單的栗子:

首先定義我們的AIDL檔案,在Android Studio建立一個AIDL檔案:

建立AIDL檔案.png

定義如下:

package com.nan.testbinder;

interface IAidlInterface {
    String pay(int pwd);
}
複製程式碼

在這個AIDL裡面,我們定義一個介面,裡面有一個pay方法來模擬支付寶的支付。輸入引數是支付密碼,返回支付結果。然後我們rebuild一下專案,AS就會自動為我們定義的AIDL檔案生成一個Java檔案,這裡我們先不詳細介紹這個檔案:

自動生成的java檔案.png

然後建立我麼的遠端服務:

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是英語,假設中國人和日本人都懂英語,那麼兩者之間就可以間接地溝通了。

下圖是跨程式通訊的基本架構:

程式間通訊模型.png

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寫資料的。

自動生成的介面類結構.png

其中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。與系統服務進行通訊。

ActivityManagerNative.png

繼續分析:

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.png

因此呼叫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等)

如果覺得我的文字對你有所幫助的話,歡迎關注我的公眾號:

公眾號:Android開發進階

我的群歡迎大家進來探討各種技術與非技術的話題,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)

相關文章