Android进阶之深入理解Activity的onActivityResult

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

答:(1)onActivityResult的用法是:ActivityA调用startActivityForResult(intent, requestCode)启动了ActivityB;当ActivityB给ActivityA回复时调用setResult设置返回的结果,然后调用finish;最后在ActivityA的onActivityResult只不过处理B的返回结果

(2)onActivityResult使用很麻烦,存在以下使用的问题:①代码处理逻辑分离,容易出现遗漏和不一致的地方;②写法不够直观,且结果数据没有类型安全保障;③当结果种类比较多时,onActivityResult会逐渐臃肿且难以维护

(3)onActivityResult不能设计成回调。假设onActivityResult可以设计成回调,由于匿名内部类会持有外部类的引用,当ActivityA被意外销毁时,由于其对象被匿名内部类OnResultCallback持有,可能会导致其无法正常被GC。当ActivityB给ActivityA回复时,OnResultCallback被调用,新的ActivityA*被恢复,但是onResult方法持有的依旧是被销毁的ActivityA的引用,回调没有任何意义

答:我觉得这是一个很有趣的问题,有趣的除了题目本身,也在于其引申出的感悟。我们在使用和学习Android Framework相关的内容时,除了要了解其内部实现机制,还要求自己要试着去反思,或者提问其设计的目的,这样才能帮助我们更好的理解和吸收其设计的精髓之处。

1.1 这道题想考察什么?

答:(1)考察要点
●是否熟悉onActivityResult的用法(初级)
●是否思考过用回调替代onActivityResult;是否实践过用回调替代onActivityResult ( 中级)
●是否意识到回调存在的问题;是否能给出匿名内部类对外部引用的解决方案(高级)

(2)题目剖析
●Activity 的onActivityResult 使用起来非常麻烦,为什么不设计成回调?
●onActivityResult是干什么的,怎么用?
●回调在这样的场景下适用吗?
●如果适用,那为什么不用回调? 如果不适用,给出你的理由?

2 onActivityResult好用吗?(onActivityResult为什么麻烦?)

答:onActivityResult使用很麻烦,存在以下使用的问题:
在这里插入图片描述

// ActivityA
 startActivityForResult(intent, requestCode);

 // ActivityB
 setResult(resultCode, intent);
 finish();

 // ActivityA
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch(resultCode) {
       ...
    }
 }

(1)代码处理逻辑分离,容易出现遗漏和不一致的地方
在使用上startActivity和onActivityResult的逻辑是分开的,例如:startActivity的requestCode被修改了,那么onActivityResult的requestCode也要修改,这样就很容易出现修改遗漏而导致不一致的地方

(2)写法不够直观,且结果数据没有类型安全保障
onActivityResult的用法要求ActivityA和ActivityB需要事先沟通好回传的内容字段和格式,如:

// ActivityB
Intent intent = new Intent();
intent.putExtra("result", "true");
setResult(resultCode, intent);
finish();

// ActivityA
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   switch(resultCode) {
      ...
      boolean result = Boolean.valueOf(data.getString("result"));
   }
}

如果ActivityB修改了result的回传格式为boolean,但是又没有通知ActivityA修改,这样编译时是不会发现问题,只有在运行时抛出异常了才发现

// ActivityB
Intent intent = new Intent();
intent.putExtra("result", true);  // String修改为boolean
setResult(resultCode, intent);

// Activity A
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   switch(resultCode) {
      ...
      // throws exception
      boolean result = Boolean.valueOf(data.getString("result"));
   }
}

(3)当结果种类比较多时,onActivityResult会逐渐臃肿且难以维护
这个问题也是显而易见的,当处理场景较多时,就会出现很多switch-case语句。当然,把相应的case实现抽成方法,可以提高代码的可读性:

// ActivityA
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   switch(resultCode) {
      case PICK_PHOTO_BACK:
          handlePickPhotoBack(data);
      case CAMERA_BACK:
          handleCameraBack(data);
      case CROP_PHOTO_BACK:
          handleCropPhotoBack(data);
      ...
   }
}

3 onActivityResult能设计成回调吗?

答:不能。
在这里插入图片描述
假设onActivityResult可以设计成回调,那么startActivity的代码应该如下:

// ActivityA
 startActivityForResult(intent, new OnResultCallback(){
    @Override
    public void onResult(Intent data) {
       textView.setText(data.getString("result"));
    }
 })

假设onActivityResult可以设计成回调,由于匿名内部类会持有外部类的引用,当ActivityA被意外销毁时,由于其对象被匿名内部类OnResultCallback持有,可能会导致其无法正常被GC。当ActivityB给ActivityA回复时,OnResultCallback被调用,新的ActivityA*被恢复,但是onResult方法持有的依旧是被销毁的ActivityA的引用,回调没有任何意义,即:

// Activity A2
 startActivityForResult(intent, new OnResultCallback(){
    @Override
    public void onResult(Intent data) {
       activityA.textView.setText(data.getString("result"));
    }
 })

在这里插入图片描述
所以,结论就是不能使用回调,因为Activity的销毁和恢复机制不允许匿名内部类出现

3.1 ActivityA销毁后的情景?

答:ActivityA被销毁,等到返回到A的时候,系统会创建一个新的ActivityA*实例,然后调用新实例的onActivityResult。ActivityB不需要知道ActivityA在不在,B只需要调用setResult设置返回的结果,然后调用finish,AMS会处理重新创建A的问题

4 基于注解处理器和Fragment的回调 通过替换匿名内部类的外部引用实现回调 方案?

答:回调的时候,ActivityA销毁了之后,它的Fragment也会被销毁。但返回的时候,即回调前一刻,系统会重新创建一个ActivityA*,并且根据之前Bundle savedInstance里面的值来恢复UI的状态,调用的是新ActivityA*的onActivityResult。而且,这时候FragmentManager当中的Fragment也会被重建,还会有一个无界面的Fragment实例来接收onActivityResult。调用的时候,我们获取到刚设置的回调进行调用,也就是匿名内部类,它持有的引用一开始其实是旧ActivityA的引用。

解决思路:(1)FragmentManager有个隐藏的方法叫findFragmentByWho,通过反射可以调用mWho去获取新的Fragment实例,获取到新的Fragment之后能获取到新的ActivityA,再通过View的id和findViewById得到ActivityA*和Fragment当中对应的View。替换回调匿名内部类当中引用的Activity 、View、Fragment的实例为重建之后的新实例,问题就解决了。(2)如果发现捕获了View 、Fragment以外的引用类型,适当给出警告或者错误来提示开发者规避就好了。
在这里插入图片描述

在这里插入图片描述

4.1 通过反射可以调用mWho去获取新的Fragment的思路?

答:FragmentManager有个隐藏的方法叫findFragmentByWho,通过反射可以调用mWho去获取新的Fragment实例
在这里插入图片描述

4.2 Activity嵌套Fragment,解决Fragment中onActivityResult()方法无响应问题?

答:(1)ActivityA中以startActivityForResult开启ActivityB,ActivityB关闭后,只会调用ActivityA的onActivityResult

(2)在ActivityA嵌套的Fragment中以startActivityForResult开启ActivityB,ActivityB关闭后,调用当前的Fragment的onActivityResult,和Activity的onActivityResult(它的requestCode非请求的requestCode,所以本质它不算合法回调);
注意:如果Activity中覆写了onActivityResult,则需要确保调用了super.onActivityResult()方法,否则Fragment的onActivityResult不会回调。

(3)在ActivityA嵌套的Fragment中以getActivity().startActivityForResult开启ActivityB,ActivityB关闭后,只会调用ActivityA的onActivityResult,不会回调Fragment的onActivityResult。

(4)所以:Activity嵌套Fragment时,可通过在Activity的onActivityResult中调用Fragment的startActivityForResult。

(5)总结起来就是:从哪里发起调用,最终就会走到哪里

Fragment中调用startActivityForResult的那些坑

4.3 基于注解处理器和Fragment的回调方案 是如何实现ActivityA中以startActivityForResult开启ActivityB,依然会调用Fragment的onActivityResult?

答:在调用框架的ActivityBuilder.startActivityForResult启动Activity时,给ResultFragment设置回调监听,以及调用ResultFragment的startActivityForResult。所以,原因就是在ActivityBuilder.startActivityForResult中调用了Fragment的startActivityForResult。
在这里插入图片描述

4.4 补充

答:1.如果它是一个值,例如整数,同时又是final的,那么这时候我们就不需要重新获取了,直接使用它的值就可以了。
2.如果它是一个引用,那么引用的对象如果跟Activity的实例没有关系,同样,我们不需要去管他,因为逻辑上这个对象不会因为Activity的变更而受到什么影响。
3.如果它是一个引用,并且跟Activity有关系,那么除了是Activity成员的情况,大概率是引用了UI,对于这种情况,我们可以根据View的id或者Fragment的mWho这个字段来获得新Activity当中对应的View或者Fragment来解决;
4.如果它是一个引用,并且跟Activity有关系,也可能指向Activity成员的成员,这种情况其实就比较复杂了,目前没有很好的解决办法,但这种情况其实很容易就可以规避掉。作为一个成熟的框架,可以在这里对回调捕获的变量做一个检查,如果发现捕获了View 、Fragment以外的引用类型,适当给出警告或者错误来提示开发者规避就好了,那么检查的方法也可以包括运行时和编译期,能采用的手段也是比较多的,又可以开始一个新的话题了。这其实也是就是最后思考题的答案。

框架仓库地址-TieGuanYin

onActivityResult能不能设计成回调

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值