Framework 原始碼解析知識梳理(1) 應用程式與 AMS 的通訊實現

澤毛發表於2017-12-21

一、前言

我們在許多和Framework解析相關的文章中,都會看到ActivityManagerService這個類,但是在上層的應用開發中,卻很少直接會使用到他。

那麼我們為什麼要學習它的呢,一個最直接的好處就是它是我們理解應用程式啟動過程的基礎,只有把和ActivityManagerService以及和它相關的類的關係都理解透了,我們才能理清應用程式啟動的過程。

今天這篇文章,我們就來對ActivityManagerService與應用程式之間的通訊方式做一個簡單的總結,這裡我們根據程式之間的通訊方向,分為兩個部分來討論:

  • 從應用程式程式到管理者程式

    (a) IActivityManager (b) ActivityManagerNative (c) ActivityManagerProxy (d) ActivityManagerService

  • 從管理者程式到應用程式程式

    (a) IApplicationThread (b) ApplicationThreadNative (c) ApplicationThreadProxy (d) ApplicationThread

二、從應用程式程式到管理者程式

在這一方向上的通訊,應用程式程式作為客戶端,而管理者程式則作為服務端。舉一個最簡單的例子,當我們啟動一個Activity,就需要通知全域性的管理者,讓它去負責啟動,這個全域性的管理者就執行在另外一個程式,這裡就涉及到了從應用程式程式到管理者程式的通訊。

這一個方向上通訊過程所涉及到的類包括:

Framework 原始碼解析知識梳理(1)   應用程式與 AMS 的通訊實現

2.1 應用程式程式向管理者程式傳送訊息

當我們想要啟動一個Activity,會首先呼叫到Activity的:

    @Override
    public void startActivityForResult(
            String who, Intent intent, int requestCode, @Nullable Bundle options) {
        //...
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, who,
                intent, requestCode, options);
        //...
    }
複製程式碼

之後呼叫到Instrumentation的:

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            //....
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            //...
    }
複製程式碼

這裡我們看到了上面UML圖中ActivityManagerNative,它的getDefault()方法返回的是一個IActivityManager的實現類:

    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            //1.得到一個代理物件
            IBinder b = ServiceManager.getService("activity");
            //2.將這個代理物件再經過一層封裝
            IActivityManager am = asInterface(b);
            return am;
        }
    };

    static public IActivityManager getDefault() {
        return gDefault.get();
    }
複製程式碼

這裡,對於gDefault變數有兩點說明:

  • 這是一個static型別的變數,因此在程式中的任何地方呼叫getDefault()方法訪問的是記憶體中的同一個物件
  • 這裡採用了Singleton模式,也就是懶漢模式的單例,只有第一次呼叫get()方法時,才會通過create()方法來建立一個物件

create()做了兩件事:

  • 通過ServieManager得到IBinder,這個IBinder是管理程式在應用程式程式的代理物件,通過IBindertransact方法,我們就可以向管理程式傳送訊息:
public boolean transact(int code, Parcel data, Parcel reply, int flags)
複製程式碼
  • IBinder傳入asInterface(IBinder b)構建一個IActivityManager的實現類,可以看到,這裡返回的是一個ActivityManagerProxy物件:
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        //這一步先忽略....
        IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        return new ActivityManagerProxy(obj);
    }
複製程式碼

下面,我們在來看一下這個ActivityManagerProxy,它實現了IActivityManager介面,我們可以看到它所實現的IActivityManager介面方法都是通過構造這個物件時所傳入的IBinder.transact(xxxx)來呼叫的,這些方法之間的區別就在於訊息的型別以及引數。

class ActivityManagerProxy implements IActivityManager {

    public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
    }

    public IBinder asBinder() {
        return mRemote;
    }

    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        //這個也很重要,我們之後分析..
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        //傳送訊息到管理者程式...
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }
 }
複製程式碼

經過上面的分析,我們用一句話總結:

應用程式程式通過ActivityManagerProxy內部的IBinder.transact(...)向管理者程式傳送訊息,這個IBinder是管理者程式在應用程式程式的一個代理物件,它是通過ServieManager獲得的。

2.2 管理者程式處理訊息

下面,我們看一下管理者程式對於訊息的處理,在管理者程式中,最終是通過ActivityManagerService對各個應用程式進行管理的。

它繼承了ActivityManagerNative類,並重寫了Binder類的onTransact(...)方法,我們前面通過ActivityManagerProxyIBinder物件傳送的訊息最終會呼叫到管理者程式中的這個函式當中,ActivityManagerNative對該方法進行了重寫:

  @Override
  public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    switch (code) {
        case START_ACTIVITY_TRANSACTION:
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String callingPackage = data.readString();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            ProfilerInfo profilerInfo = data.readInt() != 0
                    ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            //這裡在管理者程式進行處理操作....
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
      //...
  }
複製程式碼

onTransact(xxx)方法中,會根據收到的訊息型別,呼叫IActivityManager介面中所定義的不同介面,而ActivityManagerNative是沒有實現這些介面的,真正的處理在ActivityManagerService中,ActivityManagerService開始進行一系列複雜的操作,這裡之後我們介紹應用程式啟動過程的時候再詳細分析。

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }
複製程式碼

同樣的,我們也用一句話總結:

管理者程式通過onTransact(xxxx)處理應用程式傳送過來的訊息

三、從管理者程式到應用程式程式

接著,我們考慮另一個方向上的通訊方式,從管理者程式到應用程式程式,這一方向上的通訊過程涉及到下面的類:

Framework 原始碼解析知識梳理(1)   應用程式與 AMS 的通訊實現
可以看到,ApplicationThread的整個框架和上面很類似,只不過在這一個方向上,管理者程式作為客戶端,而應用程式進行則作為服務端。

3.1 管理者程式嚮應用程式程式傳送訊息

前面我們分析的時候,應用程式程式向管理者程式傳送訊息的時候,是通過IBinder這個管理者程式在應用程式程式中的代理物件來實現的,而這個IBinder則是通過ServiceManager獲取的:

IBinder b = ServiceManager.getService("activity");
複製程式碼

同理,如果管理者程式希望向應用程式程式傳送訊息,那麼它也必須設法得到一個應用程式程式在它這邊的代理物件。

我們回憶一下,在第二節的分析中,應用者程式向管理者程式傳送訊息的同時,通過writeStringBinder,放入了下面這個物件:

public int startActivity(IApplicationThread caller, ...) {
    //...
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    //...
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
}
複製程式碼

這個caller是在最開始呼叫startActivityForResult時傳入的:

    ActivityThread mMainThread;

    @Override
    public void startActivityForResult(
            String who, Intent intent, int requestCode, @Nullable Bundle options) {
        //...
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, who,
                intent, requestCode, options);
        //...
    }
複製程式碼

通過檢視ActivityThread的程式碼,我們可以看到它其實是一個定義在ApplicationThread中的ApplicationThread物件,它的asBinder實現是在ApplicationThreadNative當中:

   public IBinder asBinder() {
       return this;
   }
複製程式碼

在管理者程式接收訊息的時候,就可以通過readStrongBinder獲得這個ApplicationThread物件在管理者程式的代理物件IBinder

  @Override
  public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
      switch (code) {
        case START_ACTIVITY_TRANSACTION:
            //取出傳入的ApplicationThread物件,之後呼叫asInterface方法..
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            //...
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
            return true;
     }
  }
複製程式碼

接著,它再通過asInterface(IBinder xx)方法把傳入的代理物件通過ApplicationThreadProxy進行了一層封裝:

    static public IApplicationThread asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IApplicationThread in = (IApplicationThread) obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        return new ApplicationThreadProxy(obj);
    }
複製程式碼

之後,管理者程式就可以通過這個代理物件的transact(xxxx)方法嚮應用程式程式傳送訊息了:

class ApplicationThreadProxy implements IApplicationThread {

    private final IBinder mRemote;

    public ApplicationThreadProxy(IBinder remote) {
        mRemote = remote;
    }

    public final IBinder asBinder() {
        return mRemote;
    }

    public final void schedulePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
        //....
        mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
        //...
    }
複製程式碼

這一個過程可以總結為:

管理者程式通過ApplicationThreadProxy內部的IBinder嚮應用程式程式傳送訊息,這個IBinder是應用程式程式在管理者程式的代理物件,它是在管理者程式接收應用程式程式傳送過來的訊息中獲得的。

3.2 使用者程式接收訊息

在使用者程式程式中,ApplicationThreadonTransact(....)就可以收到管理者程式傳送的訊息,之後再呼叫ApplicationThread所實現的IApplicationThread的介面方法進行訊息的處理:

  @Override
  public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION: 
            data.enforceInterface(IApplicationThread.descriptor);
            IBinder b = data.readStrongBinder();
            boolean finished = data.readInt() != 0;
            boolean userLeaving = data.readInt() != 0;
            int configChanges = data.readInt();
            boolean dontReport = data.readInt() != 0;
            schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
            return true;
  }
複製程式碼

三、小結

以上就是應用程式程式和管理者程式之間的通訊方式,究其根本,都是通過獲取對方程式的代理物件的transact(xxxx)方法傳送訊息,而對方程式則在onTransact(xxxx)方法中進行訊息的處理,從而實現了程式之間的通訊。


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章