面试题六:Activity的onActivityResult使用起来非常麻烦,为什么不设计成回调?

Activity的onActivityResult使用起来非常麻烦,为什么不设计成回调?

一、面试官视角:这道题想考察什么?

  • 是否熟悉onActivityResult的用法
  • 是否思考过用回调替代onActivityResult
  • 是否实践过用回调替代onActivityResult
  • 是否意识到回调存在的问题
  • 是否能给出匿名内部类对外部类引用的解决方案

二、题目剖析:

1、onActivityResult是干什么的,怎么用?

干什么的:

从A页面跳转到B页面时,如果希望B页面回到A页面时,携带一些数据回来,那么就可以使用onActivityResult。

怎么用的:

1、Activity A:startActivityForResult(intent, requestCode);

2、Activity B:setResult(resultCode, intent);

3、Activity A:覆写onActivityResult(requestCode, resultCode, data)方法

存在的问题:

1、代码处理逻辑分离,容易出现遗漏和不一致的问题(有时startActivityForResult和onActivityResult调用处不在一起,经常顾此失彼,容易出现遗漏)

2、写法不够直观,且结果数据没有类型安全保障(setResult里面的intent,装载的是个Bundle,类似于HashMap,因此只有看到setResult具体的代码,才能在onActivityResult中知道回传数据的具体类型)

3、结果种类较多时,onActivityResult就会逐渐臃肿难以维护

2、回调在这样的场景下适用吗?

3、如果适用,那为什么不用回调?

4、如果不适用,给出你的理由?

假设:用回调实现

在Activity A中,通过假设出来的回调实现去调用并启动Activity B:

1
2
3
startActivityForResult(intent, new onResultCallback(){
...
});

在Activity B中:

通常情况下,Activity B启动之后总会回到Activity A,但凡事总有意外,当Activity B长时间在栈顶,而Activity A因内存不足等原因被销毁了,这个时候Activity B通过setResult(resultCode, intent)返回时,系统会重新再new一个Activity A‘,但此时的 Activity A‘ 与之前的 Activity A是完全不同的两个实例了,而我们知道匿名内部类会引用外部类的实例,那么以下代码就会存在问题:

1
2
3
4
5
6
startActivityForResult(intent, new onResultCallback(){
public void onResult(Bundle bundle) {
// 在匿名内部类中使用外部类实例去更新组件时,就会存在问题,因为此时的mTextView是之前被销毁的Activiyt A 实例里面的mTextView成员,而不是现在的 Activity A‘ 实例里面的mTextView成员了,因此这行代码是无效的,不会起任何作用,也正是有这样的问题,所以不能直接使用回调。
mTextView.setText(bundle.getStringExtra("key"));
}
});

转机:

基于注解处理器和Fragment的回调实现:

在Activity中创建一个空的Fragment并实现其回调,就算Activity和Fragment实例被销毁后重新创建了,也还是可以通过新的Fragment实例获取到新的Activity实例,在通过新的Activity实例去拿里面的mTextvView成员从而进行组件更新。

销毁被重新创建后,如何获取新的外部类实例的新的自由变量呢?因为自由变量在匿名内部类中只是一份快照,而它之前所依赖生存的外部类实例已经被销毁了,它也随之消耗,这个时候我们想找到新的外部类实例对应的新的自由变量,该怎么找呢?

通过id来找,也就是在Activity A启动Activity B的时候,将该自由变量的id存储起来,然后当Activity B返回Activity A‘ 的时候,在新的Fragment中,通过新的 Activity A‘ 的实例去findViewById来获取新的自由变量。

除了捕获View可以通过上面说的id来查找,那么Fragment如何找到新的呢?

捕获Fragment引用也可以类似通过字段mWho来替换,这个字段可以标识为唯一,但这个字段不是公开的,我们可以通过反射来获取到,然后去更新Fragment。

三、题目结论

为什么不直接使用回调:

  • onActivityResult确实麻烦

  • CallBack确实也可以简化代码编写

  • 但Activity的销毁和恢复机制不允许匿名内部类出现