startActivityForResult使用場景是什么?requestCode、resultCode含義是什么?
使用場景
用戶開始新的活動(dòng),并且希望得到新活動(dòng)的某些信息。比如選擇照片、選擇聯(lián)系人、選擇收貨地址、進(jìn)行某塊數(shù)據(jù)編輯工作等。
requestCode
解決的是「區(qū)分多個(gè)異步任務(wù)」的問題。與其他異步 API 的設(shè)計(jì)類似,如果沒有這個(gè)信息,那么 Activity 在收到響應(yīng)時(shí)會(huì)進(jìn)入混亂的狀態(tài)。比如他不知道自己得到的是選擇照片還是選擇聯(lián)系人的結(jié)果。
該信息會(huì)發(fā)送到 AMS 那邊的ActivityRecord.requestCode變量進(jìn)行記錄,Client 端新 Activity 并不知道這個(gè)信息。
- 為什么
requestCode < 0時(shí)收不到結(jié)果?
ActivityStarter 收到startActivityLocked時(shí),寫入ActivityRecord.resultTo變量為空。
if (requestCode >= 0 && !sourceRecord.finishing) {
// 只有非負(fù)數(shù)時(shí)新的 ActivityRecord 對象的 resultTo 變量才指向發(fā)起者 ActivityRecord 對象
resultRecord = sourceRecord;
}
????在 ActivityStack 收到finishActivityResultsLocked時(shí),讀取ActivityRecord.resultTo變量為空,結(jié)果數(shù)據(jù)不會(huì)添加到源ActivityRecord.results變量。
????在 ActivityStack 收到resumeTopActivityInnerLocked時(shí),讀取ActivityRecord.results數(shù)組為空,不會(huì)分發(fā)結(jié)果數(shù)據(jù),這樣源Activity也就沒有結(jié)果回調(diào)了。
resultCode
異步調(diào)用結(jié)果碼,告訴調(diào)用者成功/失敗/其它信息。
該信息由被調(diào)用 Activity/Framework 寫入,并經(jīng)過 AMS 傳遞給源 Activity。
Activity A啟動(dòng)B的時(shí)候,在B中何時(shí)執(zhí)行setResult?setResult是否可以位于Activity的finish方法之后嗎?
setResult在finish之前執(zhí)行,只是把數(shù)據(jù)記錄在Activity.mResultCode和Activity.mResultData變量中。
- 最早 Activity 構(gòu)造器階段
- 最晚 Activity.finalize 內(nèi)存回收階段
// Home 鍵 + 不保留后臺 Activity 可觸發(fā) onDestroy
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() called");
new ReqGC().start();
}
@Override
protected void finalize() throws Throwable {
Log.d(TAG, "finalize() called");
finish();
super.finalize();
}
@Override
public void finish() {
Log.d(TAG, "finish() called");
setResult(RESULT_OK, new Intent().putExtra("key", "resultData"));
super.finish();
}
// 不泄漏 Activity 對象
static class ReqGC {
public void start() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
System.gc();
handler.postDelayed(this, 10);
}
}, 10);
}
}
如果位于finish之后執(zhí)行,那信息無法傳遞給源 Activity。
從代碼可以看出setResult和finish類似生產(chǎn)者/消費(fèi)者模型,setResult負(fù)責(zé)寫入數(shù)據(jù),finish負(fù)責(zé)讀取數(shù)據(jù)。
線程安全問題
Activity.mResultCode和Activity.mResultData變量由Activity對象的鎖進(jìn)行保護(hù),支持后臺線程和 UI 線程分別進(jìn)行setResult和finish。
API設(shè)計(jì)/數(shù)據(jù)組裝問題
底層 AMS 提供的接口的參數(shù)是setResult和finish的參數(shù)的組合形式,但是 Activity 為什么把一個(gè)接口拆分成兩個(gè)接口給開發(fā)者使用?
使用方便。很多情況下調(diào)用者只關(guān)心finish,不需要理解太多的信息。
API內(nèi)部原理/數(shù)據(jù)處理流程
關(guān)鍵節(jié)點(diǎn):
- Client 端通過 AMP 把數(shù)據(jù)發(fā)送給 Server 端 AMS Binder 實(shí)體
- AMS 把數(shù)據(jù)包裝成 ActivityResult 并保存在源 ActivityRecord 的 results 變量中
- AMS 通過 ApplicationThreadProxy 向 Client 端發(fā)送 pause 信息讓棧頂 Activity 進(jìn)入 paused 狀態(tài),并等待 Client 端回復(fù)或超時(shí)
- AMS 接收 Client 端已 paused 信息,恢復(fù)下一個(gè)獲取焦點(diǎn)的 Activity ,讀取之前保存在
ActivityRecord.results變量的數(shù)據(jù)派發(fā)給 Client 端對應(yīng)的 Activity - Client 端數(shù)據(jù)經(jīng)過 ApplicationThread 對象、ActivityThread 對象的分發(fā)最后到達(dá) Activity