探索startActivity流程及在Activity間是如何傳遞Intent的

weixin_34146805發表於2018-03-06

Android進階之路系列:http://blog.csdn.net/column/details/16488.html

在activity中intent到底是怎麼傳遞的,而且還可以跨程式甚至跨app來傳遞,下面我們從原始碼層面探索一下

從startActivity開始,原始碼如下:

@Override

public void startActivity(Intent intent) {

    this.startActivity(intent, null);

}

@Override

public void startActivity(Intent intent, @Nullable Bundle options) {

    if (options != null) {

        startActivityForResult(intent, -1, options);

    } else {

        startActivityForResult(intent, -1);

    }

}

呼叫到了startActivityForResult函式,原始碼如下:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {

    startActivityForResult(intent, requestCode, null);

}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,

        @Nullable Bundle options) {

    if (mParent == null) {

        options = transferSpringboardActivityOptions(options);

        Instrumentation.ActivityResult ar =

            mInstrumentation.execStartActivity(

                this, mMainThread.getApplicationThread(), mToken, this,

                intent, requestCode, options);

        if (ar != null) {

            mMainThread.sendActivityResult(

                mToken, mEmbeddedID, requestCode, ar.getResultCode(),

                ar.getResultData());

        }

        if (requestCode >= 0) {

            mStartedActivity = true;

        }

        cancelInputsAndStartExitTransition(options);

    } else {

        if (options != null) {

            mParent.startActivityFromChild(this, intent, requestCode, options);

        } else {

            mParent.startActivityFromChild(this, intent, requestCode);

        }

    }

}

可以看到如果有parent,那麼呼叫parent的startActivityFromChild,沒有直接進行處理。可以想象parent的startActivityFromChild應該是同樣的邏輯,有興趣的可以去看看原始碼。

當沒parent時則使用mInstrumentation的execStartActivity函式來處理,mInstrumentation是一個Instrumentation物件,它的execStartActivity原始碼如下:

public ActivityResult execStartActivity(

    Context who, IBinder contextThread, IBinder token, String target,

    Intent intent, int requestCode, Bundle options) {

    IApplicationThread whoThread = (IApplicationThread) contextThread;

    if (mActivityMonitors != null) {

        synchronized (mSync) {

            final int N = mActivityMonitors.size();

            for (int i=0; i<N; i++) {

                final ActivityMonitor am = mActivityMonitors.get(i);

                if (am.match(who, null, intent)) {

                    am.mHits++;

                    if (am.isBlocking()) {

                        return requestCode >= 0 ? am.getResult() : null;

                    }

                    break;

                }

            }

        }

    }

    try {

        intent.migrateExtraStreamToClipData();

        intent.prepareToLeaveProcess(who);

        int result = ActivityManagerNative.getDefault()

            .startActivity(whoThread, who.getBasePackageName(), intent,

                    intent.resolveTypeIfNeeded(who.getContentResolver()),

                    token, target, requestCode, 0, null, options);

        checkStartActivityResult(result, intent);

    } catch (RemoteException e) {

        throw new RuntimeException("Failure from system", e);

    }

    return null;

}

在最後幾句程式碼可以看到呼叫了ActivityManagerNative.getDefault().startActivity,ActivityManagerNative.getDefault()會得到的一個ActivityManagerProxy物件(IActivityManager介面),這個類在ActivityManagerNative中,它的startActivity函式原始碼如下:

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.writeInterfaceToken(IActivityManager.descriptor);

    data.writeStrongBinder(caller != null ? caller.asBinder() : null);

    data.writeString(callingPackage);

    intent.writeToParcel(data, 0);

    data.writeString(resolvedType);

    data.writeStrongBinder(resultTo);

    data.writeString(resultWho);

    data.writeInt(requestCode);

    data.writeInt(startFlags);

    if (profilerInfo != null) {

        data.writeInt(1);

        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

    } else {

        data.writeInt(0);

    }

    if (options != null) {

        data.writeInt(1);

        options.writeToParcel(data, 0);

    } else {

        data.writeInt(0);

    }

    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

    reply.readException();

    int result = reply.readInt();

    reply.recycle();

    data.recycle();

    return result;

}

在這個函式中將一系列資訊包裝到Parcel中(包括intent),然後呼叫了mRemote.transact函式。同時,注意這裡transact的第一個引數傳的是START_ACTIVITY_TRANSACTION,這是一個code,在下面會用到。


這樣我們就碰觸到了IBinder機制,IBinder很多文章都分析過,這裡根據我的理解大概簡單講一下。

IBinder機制主要實現在底層(c++),通過它來實現程式間的通訊.

簡單來說是一端作為service,另一端作為client(實際上都是service,只不過在不同的情況下角色不同,都可以轉換)

(1)訊息發出方,即客戶端的service通過BpBinder(在這個流程中就是程式碼中的mRemote)開啟/dev/binder裝置(linux的一塊核心空間,實際上就是共享記憶體),並將資訊寫入(即IBinder的transact函式)。

(2)訊息接收方,即服務端的service會開啟迴圈執行緒(其實從註冊後就開始了),通過BBinder(在這個流程中是一個ActivityManagerNative物件,下面會講到)不停的從/dev/binder裝置中讀取資訊,然後根據資訊做相應的處理(IBinder的onTransact函式)

(3)這時當原客戶端從/dev/binder裝置得到對方已收到的資訊後,角色轉換為服務端,等待回覆即replay(同樣迴圈執行緒)。

(4)當原服務端處理後需要回復時,角色轉為客戶端,傳送回覆。

這樣就實現了跨程式的通訊。

因為是通過binder,所以我們的訊息如intent之類都要包裝成binder支援的Parcel,這也是intent只能傳遞可系列化物件的原因。同時因為這塊共享記憶體大小的限制,也就導致了intent傳遞資料的大小限制(關於Parcel和大小限制請閱讀:Android中Intent/Bundle的通訊原理及大小限制一文)。

同樣因為binder機制,我們不僅可以啟動本app中的activity(自己同時是客戶端和服務端),也可以啟動其他app中公開的acitivity。


ActivityManagerNative類實際上繼承了Binder(IBinder介面),也就是上面提到的BBinder的角色。當接收並解析binder訊息後會呼叫它的onTransact來進行處理,在ActivityManagerNative中onTransact非常龐大,根據code做不同的處理,我們只需要關注START_ACTIVITY_TRANSACTION這個code即可。

@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;

    }

    ...

}

拿到Parcel型別的資料,然後反系列化解析成Intent等,最後呼叫startActivity,但是在ActivityManagerNative並沒有找到這個函式,那是因為ActivityManagerNative是一個抽象類,真正的實現是在它的子類ActivityManagerService中,然後通過ApplicationTreadProxy等類啟動activity。這部分不是我們本文的重點,而且比較複雜,就不詳細說了。借用網上一張時序圖來展示一下這個流程。

2030586-87d567f3ee90e2a8
image

Android進階之路系列:http://blog.csdn.net/column/details/16488.html

相關文章