引言
假设现在有一个 Activity A
,从 Activity A
通过 startActivityForResult
方法启动了 Activity B
,在 Activity B
销毁前,Activity B
想将一些数据回传给 Activity A
,那么可以主动调用 setResult
方法,这样在 Activity B
销毁后,Activity A
重新展示时,在 Activity A
的 onActivityResult
方法就能够获取 Activity B
回传过来的数据。
上面是 Activity
回传数据的一个流程,我们也非常的熟悉了,整体流程如下图所示:
一个例子
setResult方法的使用还是比较简单的,但是使用不当那很有可能会踩到坑。我们来看下面一个例子。
public class MainActivity extends Activity {
public static final String TAG = "MyApplication";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, 0);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 100) {
Log.d(TAG, "MainActivity onActivityResult");
}
}
}
MainActivity
作为启动页,点击触发 startActivityForResult
跳转到 SecondActivity
,并在 onActivityResult
方法监听 SecondActivity
传递回来的数据。
public class SecondActivity extends Activity {
public static final String TAG = "MyApplication";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
//setResult();
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "SecondActivity onResume");
//setResult();
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "SecondActivity onPause");
setResult();
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "SecondActivity onStop");
//setResult();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "SecondActivity onDestroy");
//setResult();
}
@Override
public void finish() {
super.finish();
Log.d(TAG, "SecondActivity finish");
}
private void setResult() {
setResult(100);
Log.d(TAG, "SecondActivity setResult");
}
}
SecondActivity在其各个生命周期中分别调用setResult给MainActivity返回数据,当用户在SecondActivity点击返回键返回时,根据Log信息跟踪方法的调用路径。例如上面的例子中在SecondActivity的onPause方法调用setResult,得到的Log如下图所示:
从上图可以看到当在 onPause
方法执行 setResult
方法时,在执行 onPause
之前 SecondActivity
就已经执行了 finish
方法了,因此即使 SecondActivity
成功执行了 setResult
方法,但 MainActivity
的 onActvivityResult
方法还是没有收到 SecondActivity
的返回信息。同理在 onStop
、onDestroy
中执行 setResult
也是不可以的,但是在 onPause
方法前执行 setResult
就能顺利的将 SecondActivity
的信息传递给 MainActivity
。如下图所示就是在 onResume
方法中执行 setResult
的Log信息。
源码解析
setResult
方法由 Activity
类提供,不管是调用 setResult(int resultCode)
或者 setResult(int resultCode, Intent data)
,最终都是赋值操作,将 resultCode
赋值给 mResultCode
,将 data
赋值给 mResultData
。
public final void setResult(int resultCode, Intent data) {
synchronized (this) {
mResultCode = resultCode;
mResultData = data;
}
}
从上面的例子中可以知道,setResult
是否发送成功与 finish
方法有很大的关系。我们就此分析一下 finish
方法。finish
方法又调用了 finish(int finishTask)
方法。
private void finish(int finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
if (ActivityManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
}
}
...
}
finish
方法中引用了 mResultCode
和 mResultData
,然后调用了 AMS
的 finishActivity
方法。
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
...
try {
...
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
}
return res;
}
}
finish
方法中通过 tr.getStack
方法返回 ActivityStack
,并执行 ActivityStack
的 requestFinishActivityLocked
方法。requestFinishActivityLocked
方法里又执行了 finishActivityLocked
方法。
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj, boolean pauseImmediately) {
...
finishActivityResultsLocked(r, resultCode, resultData);
...
}
finishActivityLocked
方法中执行了 finishActivityResultsLocked
方法。
private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
// send the result
ActivityRecord resultTo = r.resultTo;
if (resultTo != null) {
if (resultTo.userId != r.userId) {
if (resultData != null) {
resultData.prepareToLeaveUser(r.userId);
}
}
if (r.info.applicationInfo.uid > 0) {
mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
resultTo.packageName, resultData,
resultTo.getUriPermissionsLocked(), resultTo.userId);
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
resultData);
r.resultTo = null;
}
...
}
finishActivityResultsLocked
方法的注释 send the result
已经表明了该方法会将 setResult
信息传递到原始启动 Activity
。finishActivityResultsLocked
的第一个参数 ActivityRecord
对象代表的是当前 Activity
(相当于上面例子的 SecondActivity
),它的 resultTo
表示另外一个 ActivityRecord
对象,代表的是原始启动 Activity
(相当于上面例子的 MainActivity
),找到原始启动 ActivityRecord
对象后,就执行了 ActivityRecord
的 addResultLocked
方法。
void addResultLocked(ActivityRecord from, String resultWho,
int requestCode, int resultCode,
Intent resultData) {
ActivityResult r = new ActivityResult(from, resultWho,
requestCode, resultCode, resultData);
if (results == null) {
results = new ArrayList<ResultInfo>();
}
results.add(r);
}
addResultLocked
方法中构造了一个 ActivityResult
对象,并将其存储在 ActivityRecord
对象的一个 ArrayList
对象中。这也表明原始启动的 Activity
可以接收多个由其启动的 Activity
传递回来的 resultCode
或 resultData
。
由上面的分析可知,在当前 Activity
执行 finish
时会将 setResult
方法中的 resultCode
或resultData
存储到它的原始启动 Activity
的 ActivityRecord
对象中。所以我们得出这样的一个结论:
-
setResult
方法必须要在finish
方法之前执行,否则setResult
方法执行无效。
所以在上面例子中,在 onPause
、onStop
、onDestroy
方法执行 setResult
方法, 为什么MainActivity
的 onActivityResult
执行不到???
因为 onPause
、onStop
、onDestroy
方法执行之前 finish
方法已经执行了,因此 setResult
方法执行无效。
至此已经分析完了调用 Activity
调用 setResult
方法后,数据存储到原始 Activity
的 ActivityRecord
对象的整个流程,但什么时候触发原始 Activity
的 onActivityResult
方法执行呢?这就涉及到 Activity
的生命周期方法了。
熟悉 Activity
的生命周期方法的同学都知道,生命周期方法都是在 ActivityThread
中分发的。对于 Activity
的 onActivityResult
方法的执行,在 ActivityThread
中会先执行 handleSendResult
方法。
@Override
public void handleSendResult(IBinder token, List<ResultInfo> results, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
...
deliverResults(r, results, reason);
...
}
}
handleSendResult
方法的第二个参数 results
就是上面我们分析的 ActivityRecord
对象中的 ArrayList\<ResultInfo\> results
,方法里又调用了 deliverResults
方法。
private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) {
final int N = results.size();
for (int i=0; i<N; i++) {
ResultInfo ri = results.get(i);
try {
if (ri.mData != null) {
ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
ri.mData.prepareToEnterProcess();
}
r.activity.dispatchActivityResult(ri.mResultWho,
ri.mRequestCode, ri.mResultCode, ri.mData, reason);
} catch (Exception e) {
...
}
}
}
在 deliverResults
方法中,遍历了List<ResultInfo> results
,并调用 Activity
的dispatchActivityResult
方法,该方法最终会执行 Activity
的 onActvivityResult
方法,并将 resultCode
或 resultData
传递过去。
总结
setResult
方法用来将数据回传给其启动页的 Activity
,但是它的执行时机是有讲究的,我们必须保证在 finish
方法之前调用 setResult
方法,也同时需要避免在 onPause
、onStop
、onDestroy
方法中调用 setResult
,否则 setResult
方法即使被执行了,数据也不能回传给其启动页 Activity
。