引言

假设现在有一个 Activity A,从 Activity A 通过 startActivityForResult 方法启动了 Activity B,在 Activity B 销毁前,Activity B 想将一些数据回传给 Activity A,那么可以主动调用 setResult 方法,这样在 Activity B 销毁后,Activity A 重新展示时,在 Activity AonActivityResult 方法就能够获取 Activity B 回传过来的数据。

上面是 Activity 回传数据的一个流程,我们也非常的熟悉了,整体流程如下图所示:


一个例子

setResult方法的使用还是比较简单的,但是使用不当那很有可能会踩到坑。我们来看下面一个例子。

public class MainActivity extends Activity {public static final String TAG ="MyApplication";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = new Intent(MainActivity.this, SecondActivity.class);startActivityForResult(intent, 0);}});}@Overrideprotected 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";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.second_layout);//setResult();}@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "SecondActivity onResume");//setResult();}@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "SecondActivity onPause");setResult();}@Overrideprotected void onStop() {super.onStop();Log.d(TAG, "SecondActivity onStop");//setResult();}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "SecondActivity onDestroy");//setResult();}@Overridepublic 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 方法,但 MainActivityonActvivityResult 方法还是没有收到 SecondActivity 的返回信息。同理在 onStoponDestroy 中执行 setResult 也是不可以的,但是在 onPause 方法前执行 setResult 就能顺利的将 SecondActivity 的信息传递给 MainActivity 。如下图所示就是在 onResume 方法中执行 setResult 的Log信息。


源码解析

setResult 方法由 Activity 类提供,不管是调用 setResult(int resultCode) 或者 setResult(int resultCode, Intent data),最终都是赋值操作,将 resultCode 赋值给 mResultCode,将 data 赋值给 mResultData

public final voidsetResult(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 方法中引用了 mResultCodemResultData,然后调用了 AMSfinishActivity 方法。

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,并执行 ActivityStackrequestFinishActivityLocked 方法。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 resultActivityRecord 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 信息传递到原始启动 ActivityfinishActivityResultsLocked 的第一个参数 ActivityRecord 对象代表的是当前 Activity(相当于上面例子的 SecondActivity),它的 resultTo 表示另外一个 ActivityRecord 对象,代表的是原始启动 Activity(相当于上面例子的 MainActivity),找到原始启动 ActivityRecord 对象后,就执行了 ActivityRecordaddResultLocked 方法。

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 传递回来的 resultCoderesultData

由上面的分析可知,在当前 Activity 执行 finish 时会将 setResult 方法中的 resultCoderesultData 存储到它的原始启动 ActivityActivityRecord 对象中。所以我们得出这样的一个结论:

  • setResult 方法必须要在 finish 方法之前执行,否则 setResult 方法执行无效。

所以在上面例子中,在 onPauseonStoponDestroy 方法执行 setResult 方法, 为什么MainActivityonActivityResult 执行不到???

因为 onPauseonStoponDestroy 方法执行之前 finish 方法已经执行了,因此 setResult 方法执行无效。

至此已经分析完了调用 Activity 调用 setResult 方法后,数据存储到原始 ActivityActivityRecord 对象的整个流程,但什么时候触发原始 ActivityonActivityResult 方法执行呢?这就涉及到 Activity 的生命周期方法了。

熟悉 Activity 的生命周期方法的同学都知道,生命周期方法都是在 ActivityThread 中分发的。对于 ActivityonActivityResult 方法的执行,在 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,并调用 ActivitydispatchActivityResult方法,该方法最终会执行 ActivityonActvivityResult 方法,并将 resultCoderesultData 传递过去。

总结

setResult 方法用来将数据回传给其启动页的 Activity,但是它的执行时机是有讲究的,我们必须保证在 finish 方法之前调用 setResult 方法,也同时需要避免在 onPauseonStoponDestroy 方法中调用 setResult,否则 setResult 方法即使被执行了,数据也不能回传给其启动页 Activity

【源码解析】Activity之setResult工作原理相关推荐

  1. Android View体系(六)从源码解析Activity的构成

    前言 本来这篇是要讲View的工作流程的,View的工作流程主要指的measure.layout.draw这三大流程,在讲到这三大流程之前我们有必要要先了解下Activity的构成,所以就有了这篇文章 ...

  2. CoreCLR源码探索(八) JIT的工作原理(详解篇)

    在上一篇 我们对CoreCLR中的JIT有了一个基础的了解,这一篇我们将更详细分析JIT的实现. JIT的实现代码主要在https://github.com/dotnet/coreclr/tree/m ...

  3. 4、Eureka 源码解析 之 Eureka Client 启动原理分析

    在前面的一篇文章 3.Eureka 源码解析 之 Eureka Server 启动原理分析当中我们分析了一下 Eureka Server 的启动.在集群环境下 Eureka Server 相互之前需要 ...

  4. Android源码分析—属性动画的工作原理

    转载请注明出处: http://blog.csdn.net/singwhatiwanna/article/details/17853275 前言 本文为Android动画系列的最后一篇文章,通过对源码 ...

  5. greenplum 源码解析 FTS辅助进程工作主流程

    1 简介   FTS(Fault Tolerance Service)是greenplum提供的对于子节点的故障检测与恢复的服务.其隶属于master的一个子进程,通过定期轮询每个primary的状态 ...

  6. 【Vue.js源码解析 一】-- 响应式原理

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 课程目标 Vue.js 的静态成员和实例成员初始化过程 首次渲染的过程 数据响应式原理 – 最核心的特性之一 准备工作 ...

  7. 从 Android 6.0 源码的角度剖析 Binder 工作原理 | CSDN 博文精选

    在从Android 6.0源码的角度剖析Activity的启动过程一文(https://blog.csdn.net/AndrExpert/article/details/81488503)中,我们了解 ...

  8. Android属性动画赏析,Android源码分析—属性动画的工作原理

    前言 本文为Android动画系列的最后一篇文章,通过对源码的分析,能够让大家更深刻地理解属性动画的工作原理,这有助于我们更好地使用属性动画.但是,由于动画的底层实现已经深入到jni层,并且涉及到显示 ...

  9. matlabeig函数根据什么原理_vue3.0 源码解析二 :响应式原理(下)

    一 回顾上文 上节我们讲了数据绑定proxy原理,vue3.0用到的基本的拦截器,以及reactive入口等等.调用reactive建立响应式,首先通过判断数据类型来确定使用的hander,然后创建p ...

最新文章

  1. hdu 1561 The more, The Better_树状dp
  2. 02.iOS开发网络篇—HTTP协议
  3. 在网上收集了一部分关于使用Google API进行手机定位的资料和大家分享
  4. 自动驾驶路径轨迹规划(三阶曲线spline)
  5. Hadoop MapReduce编程 API入门系列之压缩和计数器(三十)
  6. VMWare虚拟机三种网络形式
  7. 借助阿里AntUI元素实现两个Web页面之间的过渡——“Loading…”
  8. 有k个list列表, 各个list列表的元素是有序的,将这k个列表元素进行排序( 基于堆排序的K路归并排序)...
  9. ccf-csp认证历年真题(持续更新)
  10. 自媒体爆文神器——必备爆文写作工具
  11. MDIO总线介绍 |CSDN创作打卡
  12. python excel数据处理教程pdf_python对excel操作详解.pdf
  13. 解决three.js渲染gltf 模型与gltfViewer网站效果不一致问题 krpano发黑问题 three.js gltf模型渲染发黑问题
  14. Ubuntu 16.04 64位 安装 modelsim
  15. Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backs
  16. idea报错:Parent ‘Unknown:Unknown:Unknown‘ has problems
  17. matlab怎么输出图像文件夹,Matlab读取图片 显示和保存图像的相关操作
  18. VMware上创建虚拟机以及安装操作系统
  19. 深度解剖数据在内存中的存储!!数据在内存中原来是这样表示的?为什么浮点数和整数的存储方式差距这么大?
  20. 如果出现自己写法跟学习资料一样,但是还是运行失败,那么DEV需要配置C++环境

热门文章

  1. DatePicker日期选择器
  2. hello new one
  3. 玩转金蝶K3 WISE API接口
  4. 安装Xshell并使用其进行Ymodem协议的串口传输
  5. 红黑树原理解析以及Java实现
  6. 基于J2EE的B2C电子商务系统开发(论文+系统+开题报告+文献综述+任务书+答辩PPT+中期报表+外文文献+说明书)
  7. 基于RS422通信的FPGA软件设计第一天
  8. 计算机组成原理移位寄存器实验报告,移位运算器实验报告
  9. ug的服务器安装在哪个位置,ug可以安装在云服务器吗
  10. SQL severe 存储过程例题