探索startActivity流程及在Activity間是如何傳遞Intent的
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。這部分不是我們本文的重點,而且比較複雜,就不詳細說了。借用網上一張時序圖來展示一下這個流程。
Android進階之路系列:http://blog.csdn.net/column/details/16488.html
相關文章
- 使用Intent傳遞物件Intent物件
- 探究intent傳遞大小限制Intent
- Activity、Fragment和IntentFragmentIntent
- 將 Intent 序列化,像 Uri 一樣傳遞 Intent!!!Intent
- 從0系統學Android-2.3使用 Intent 在 Activity 之間穿梭AndroidIntent
- Android Intent 傳遞資料大小限制AndroidIntent
- Intent.FLAG_ACTIVITY_NEW_TASKIntent
- context裡的超時時間是怎麼在微服務之間傳遞的Context微服務
- JavaScript 是如何工作的:JavaScript 的共享傳遞和按值傳遞JavaScript
- Activity跳轉時傳遞資料的騷操作
- 3-AVI–Activity與Fragment的資料傳遞Fragment
- emoji在瀏覽器中是如何傳遞給伺服器的瀏覽器伺服器
- Android Activity是如何啟動的?Activity的生命週期是如何呼叫的?Android
- 如何確認DFMEA的傳遞是有效的?
- Java - 是值傳遞還是引用傳遞Java
- 面試官問:Go 中的引數傳遞是值傳遞還是引用傳遞?面試Go
- HttpRunner3的變數是如何傳遞的HTTP變數
- Flutter:學會在頁面間傳遞引數Flutter
- postman(五):在不同介面之間傳遞資料Postman
- AbilitySlice之間的傳遞值
- 一人一貓旅行記之Intent傳遞資料原理Intent
- chan中傳遞map資料,傳遞的是引用
- 解惑4:java是值傳遞還是引用傳遞Java
- Java是值傳遞還是引用傳遞,又是怎麼體現的Java
- Vue父子之間的值傳遞Vue
- vue---元件間傳遞訊息(父子傳遞訊息,兄弟傳遞訊息)Vue元件
- Activity 是如何載入佈局的?
- Activity和fragment是如何互動的Fragment
- vue元件之間的資料傳遞Vue元件
- Netty使用及事件傳遞Netty事件
- 引數傳遞方式必須是const引用傳遞
- Vue元件間傳遞資料Vue元件
- Vue元件間資料傳遞Vue元件
- 如何在HarmonyOS NEXT中處理頁面間的資料傳遞?
- Application,Activity,Service的建立流程(1)APP
- Application,Activity,Service的建立流程(2)APP
- Activity 流程模型匯入匯出-activity流程模型匯入匯出模型
- 說說在 Python 中如何傳遞任意數量的實參Python