(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

问题

Android中,通过startActivityForResult跳转页面获取数据应该不必多说,但是这种所有获取到的结果都需要到onActivityResult中处理的方式实在令人蛋疼。

试想一下,我们敲着代码唱着歌。突然,半路上跳出一群马匪,让我们到另一个页面获取一点数据,获取后还不让在当前代码位置处理逻辑,要去onActivityResult添加一个requestCode分支处理结果,处理完才让回来,等这一切都做完回来难免就会陷入这样的思考:我是谁,我在哪,我在干什么,我刚才写到哪了……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

再想一下,你跟同事的代码,跟到一个startActivityForResult,于是不耐烦地ctrl+f找到onActivityResult,发现里面充斥着大量的requestCode分支,然后突然意识到刚才没记下requestCode是什么……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

分析问题

问题的根源是所有处理结果的逻辑都要放到onActivityResult中,在里面根据requestCode作不同处理。而我们渴望的是能在发起startActivityForResult的时候捎带着把获取结果后处理的逻辑也传进去,并能在内部对requestCode判断好,不用我们再判断一遍。

解决问题

尝试一(不完美方式)

新建一个OnResultManager类,用来管理获取结果的回调,下面详细说。

分析问题时说了,我们希望在发起startActivityForResult的时候就指定好处理结果的逻辑,这个简单,在OnResultManager中创建一个Callback接口,里面定义一个OnActivityResult方法,参数和Activity的OnActivityResult方法参数完全一样,在发起start的时候除了intent和requestCode,再传一个callback进去。而OnresultManager负责控制在系统的onActivityResult触发时,调用对应callback的方法。

下面是OnResultManager的全部代码。

public class OnResultManager {
    private static final String TAG = "OnResultManager";
    //HashMap的key Integer为requestCode
    private static WeakHashMap<Activity,HashMap<Integer,Callback>> mCallbacks = new WeakHashMap<>();
    private WeakReference<Activity> mActivity;

    public OnResultManager(Activity activity) {
        mActivity = new WeakReference<Activity>(activity);
    }

    public void startForResult(Intent intent, int requestCode, Callback callback){
        Activity activity = getActivity();
        if(activity == null){
            return;
        }

        addCallback(activity,requestCode,callback);
        activity.startActivityForResult(intent,requestCode);
    }

    public void trigger(int requestCode, int resultCode, Intent data){
        Log.d(TAG,"----------- trigger");
        Activity activity = getActivity();
        if(activity == null){
            return;
        }

        Callback callback = findCallback(activity,requestCode);
        if(callback != null){
            callback.onActivityResult(requestCode,resultCode,data);
        }
    }

    //获取该activity、该requestCode对应的callback
    private Callback findCallback(Activity activity,int requestCode){
        HashMap<Integer,Callback> map = mCallbacks.get(activity);
        if(map != null){
            return map.remove(requestCode);
        }
        return null;
    }

    private void addCallback(Activity activity,int requestCode,Callback callback){
        HashMap<Integer,Callback> map = mCallbacks.get(activity);
        if(map == null){
            map = new HashMap<>();
            mCallbacks.put(activity,map);
        }
        map.put(requestCode,callback);
    }

    private Activity getActivity(){
        return mActivity.get();
    }

    public interface Callback{
        void onActivityResult(int requestCode, int resultCode, Intent data);
    }
} 

逻辑很简单,里面持有一个mActivity,使用弱引用以防止内存泄漏,在构造器中为其赋值。还有一个static的WeakHashMap<Activity,HashMap<Integer,Callback>> mCallbacks 用来存放所有的callback,先以activity分,在各个activity中又使用hashmap存储requestCode和callback的对应关系。

在startForResult时,最终还是调用的activity的startActivityForResult,只不过在跳转页面之前,把callback存入了mCallbacks中。

而trigger方法则是根据activity和requestCode从mCallbacks中取出对应的callback,调用方法。

现在callback的存和取都搞定了,那么问题来了,什么时候触发“取”的操作呢,即trigger方法怎么触发呢?答案是在onActivityResult中调用,嫌麻烦可以在BaseActivity中调用。

使用示例:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        go.setOnClickListener {
            val intent = Intent(this,SecondActivity::class.java)
            onResultManager.startForResult(intent,REQUEST_CODE,{requestCode: Int, resultCode: Int, data: Intent? ->
                if (resultCode == Activity.RESULT_OK){
                    val text = data?.getStringExtra("text")
                    Toast.makeText(this,"result -> "+text,Toast.LENGTH_SHORT).show()
                }else{
                    Toast.makeText(this,"canceled",Toast.LENGTH_SHORT).show()
                }
            })
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        onResultManager.trigger(requestCode,resultCode,data)
    } 

可是这样好蠢啊,你是不是觉得要是不用手动触发,能自动触发多好。我也是这么想的,所以有整整一天我一直在找有什么办法能hook到onActivityResult方法,最后hook了Instrumentation,也hook了AMS,但是都对这个onActivityResult无能为力,看源码发现好像是在ActivityThread中传递的,但是很不幸的是这个ActivityThread没办法hook,至少通过简单的反射和代理没办法做到(如果谁有办法hook到,恳请您能分享出来,我真的特别想知道,我不甘心啊)

更新,强迫症福音,onActivityResult方法hook到了

这里感谢一下@world_hello的提醒,十分感谢。

之前看到ActivityThread的mH的时候总想着弄个代理继承它,然后重写handleMessage方法,来获取结果信息,可是却苦于继承不了,其实我一直忽视了Handler内部的一个Callback接口,其实完全不用代理。下面详细讲解一下。

先说一下Handler,我们一般使用的时候大多是继承自Handler,然后重写handleMessage方法,在里面处理接收消息。其实Handler还有个构造函数可以接收一个Handler.Callback(不要和我们自己定义的callback搞混了啊),在Handler.Callback的handleMessage方法中处理消息。

看一下Handler源码:

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    } 

可以看到,如果mCallback不为null,就会执行mCallback的handleMessage方法。如果这个handleMessage方法的返回值为true,就会直接return,如果为false,就会继续执行Handler本身的handleMessage方法。

下面打开Activity源码,startActivityForResult方法中有这么一段

if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            } 

ar是ActivityResult,如果不为null,就调用mMainThread的sendActivityResult方法,这个mMainThread就是ActivityThread类型的,所以继续打开ActivityThread源码,找到这个方法,顺着这个方法一直找一直找,这里中间的方法我就不贴了,最后会找到一个叫sendMessage的方法,这个方法最后一行是

mH.sendMessage(msg); 

这个mH是一个Handler的子类,所以很明显,activityresult在这个环节是通过handler传递的。

在H的handleMessage方法中有这样一个case分支

case SEND_RESULT:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
                    handleSendResult((ResultData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break; 

不用再多说了吧,就是这里了。它调用了handleSendResult((ResultData)msg.obj),所以这个ResultData应该就是存放了我们需要的东西。只要能拦截到它,拿出我们需要的数据,那就算是hook到了onActivityResult了,那么怎么拿到呢?现在就要提到我们刚才提的Handler.Callback了,我们创建一个callback,并记得让handleMessage的方法返回false,以免影响mH本身的handleMessage方法的执行,然后通过反射把这个callback给mH set进去。下面需要您对反射有一点点了解。

public class HookUtil {
    public static void hookActivityThreadHandler() throws Exception {
        // 先获取到当前的ActivityThread对象
        final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
        currentActivityThreadField.setAccessible(true);
        final Object currentActivityThread = currentActivityThreadField.get(null);

        // 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
        Field mHField = activityThreadClass.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(currentActivityThread);
        
        Handler.Callback mHCallback = new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if(msg.what == 108){
                    Log.d("hook-------","onActivityResult");

                    try{
                        Object resultData = msg.obj;

                        Field mActivitiesField = activityThreadClass.getDeclaredField("mActivities");
                        mActivitiesField.setAccessible(true);
                        ArrayMap mActivities = (ArrayMap) mActivitiesField.get(currentActivityThread);

                        Class<?> resultDataClass = Class.forName("android.app.ActivityThread$ResultData");
                        Field tokenField = resultDataClass.getDeclaredField("token");
                        tokenField.setAccessible(true);
                        IBinder token = (IBinder) tokenField.get(resultData);

                        //r是ActivityClientRecord类型的
                        Object r = mActivities.get(token);
                        Class<?> ActivityClientRecordClass = Class.forName("android.app.ActivityThread$ActivityClientRecord");
                        Field activityField = ActivityClientRecordClass.getDeclaredField("activity");
                        activityField.setAccessible(true);
                        Activity activity = (Activity) activityField.get(r); //至此,终于拿到activity了

                        Field resultsField = resultDataClass.getDeclaredField("results");
                        resultsField.setAccessible(true);
                        List results = (List) resultsField.get(resultData);

                        //ResultInfo类型
                        Object resultInfo = results.get(0);

                        Class<?> resultInfoClass = Class.forName("android.app.ResultInfo");
                        Field mRequestCodeField = resultInfoClass.getDeclaredField("mRequestCode");
                        mRequestCodeField.setAccessible(true);
                        int mRequestCode = (int) mRequestCodeField.get(resultInfo); //拿到requestCode

                        Field mResultCodeField = resultInfoClass.getDeclaredField("mResultCode");
                        mResultCodeField.setAccessible(true);
                        int mResultCode = (int) mResultCodeField.get(resultInfo); //拿到resultCode

                        Field mDataField = resultInfoClass.getDeclaredField("mData");
                        mDataField.setAccessible(true);
                        Intent mData = (Intent) mDataField.get(resultInfo); //拿到intent

                        new OnResultManager(activity).trigger(mRequestCode,mResultCode,mData);



                    }catch (Exception e){
                        e.printStackTrace();
                    }




                }

                return false;
            }
        };
        Field mCallBackField = Handler.class.getDeclaredField("mCallback");
        mCallBackField.setAccessible(true);

        mCallBackField.set(mH, mHCallback);

    }

} 

hookActivityThreadHandler方法中就是先获取到ActivityThread对象,再通过activityThread获取到mH,然后创建了一个Handler.Callback,最后用反射把这个callback set给mH。

相比之下,Handler.Callback中的handleMessage方法看起来更长一些,但其实并不复杂,只是反射使它变得面目全非了,它主要就是为了获取我们OnResultManager的trigger方法需要的数据,一个activity,以及requestCode、resultCode、data。

msg.what==108的判断,这里108就是SEND_RESULT,我图方便直接写死了,源码里看着为108,当然通过反射动态获取SEND_RESULT更规范一点,大家别在意这些细节。

先看activity怎么获取,主要看ActivityThread的handleSendResult方法,它的第一行

 private void handleSendResult(ResultData res) {
        ActivityClientRecord r = mActivities.get(res.token); 

这个res就是msg.obj,而获取到ActivityClientRecord之后,它有一个名为activity的field,获取的就是我们需要的activity对象了。本身很简单,只不过这短短的一行代码要用反射一点一点获取,就繁琐一些了。

另外三个数据都在一起,在ResultData里有个名为results的Field,它是一个List,里面就一个元素(可能有误,但我打断点看它一直都是一个),是ResultInfo类型,我们需要的requestCode、resultCode、data就都在这个resultInfo里面了,所以,继续反射。

都获取完了,调用

new OnResultManager(activity).trigger(mRequestCode,mResultCode,mData); 

就是把我们之前手动写在onActivityResult中的那句放到这里自动触发。

现在方法写完了,还要写个自定义Application,在onCreate中调用

HookUtil.hookActivityThreadHandler() 

大功告成啦!,赶紧测试一下吧,现在终于不用再在onActivityResult中手动触发啦!github上代码已经更新。

按理说我们该在OnResultManager中定义个init方法,然后在init方法中去调hookActivityThreadHandler貌似更规范一些,不过这只算个demo,就不考虑这么多了。

还有,这种通过反射hook的方法在稳定性和兼容性上都无法保证,所以这算是一种仅供娱乐的方式吧!领略一下hook的魅力。

OnResultManager项目地址

下面是world_hello实现的hook,虽然思路是一样的,但是编写的要比我的容易看懂得多,自愧不如,在此强烈推荐大家看一下。

world_hello实现的hook项目地址

第二种方式(参考RxPermissions的做法)

前段时间又来了个小项目,领导扔给我了,然后在这个项目里就把之前没用过(没错,之前总用H5开发……)的rxjava、kotlin都加进来了。有一天做动态权限处理,惊奇地发现RxPermissions居然摆脱了Activity的onRequestPermissionsResult方法!!!大家都知道,动态权限大体就是先调用requestPermissions方法,然后授权的结果要到onRequestPermissionsResult中处理,简直和startActivityForResult如出一辙。那RxPermissions是怎么做到的呢!!!

接着在前几天不太忙的时候看了下RxPermissions的源码,发现它内部持有一个Fragment,这个fragment没有视图,只负责请求权限和返回结果,相当于一个桥梁的作用,我们通过rxPermissions发起request的时候,其实并不是activity去request,而是通过这个fragment去请求,然后在fragment的onRequestPermissionsResult中把结果发送出来,如此来避开activity的onRequestPermissionsResult方法。

当时,没见过什么大场面的我差点就给跪了。

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

RxPermissions的源码就不贴了,网上的讲解应该也很多。

同样,Fragment也有startActivityForResult方法啊,那我们也可以采取类似的方法,为所欲为。

这次取名叫AvoidOnResult,主要就是AvoidOnResult和AvoidOnResultFragment两个类。先上代码:

public class AvoidOnResult {
    private static final String TAG = "AvoidOnResult";
    private AvoidOnResultFragment mAvoidOnResultFragment;

    public AvoidOnResult(Activity activity) {
        mAvoidOnResultFragment = getAvoidOnResultFragment(activity);
    }

    public AvoidOnResult(Fragment fragment){
        this(fragment.getActivity());
    }

    private AvoidOnResultFragment getAvoidOnResultFragment(Activity activity) {
        AvoidOnResultFragment avoidOnResultFragment = findAvoidOnResultFragment(activity);
        if (avoidOnResultFragment == null) {
            avoidOnResultFragment = new AvoidOnResultFragment();
            FragmentManager fragmentManager = activity.getFragmentManager();
            fragmentManager
                    .beginTransaction()
                    .add(avoidOnResultFragment, TAG)
                    .commitAllowingStateLoss();
            fragmentManager.executePendingTransactions();
        }
        return avoidOnResultFragment;
    }

    private AvoidOnResultFragment findAvoidOnResultFragment(Activity activity) {
        return (AvoidOnResultFragment) activity.getFragmentManager().findFragmentByTag(TAG);
    }

    public Observable<ActivityResultInfo> startForResult(Intent intent, int requestCode) {
        return mAvoidOnResultFragment.startForResult(intent, requestCode);
    }

    public Observable<ActivityResultInfo> startForResult(Class<?> clazz, int requestCode) {
        Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
        return startForResult(intent, requestCode);
    }

    public void startForResult(Intent intent, int requestCode, Callback callback) {
        mAvoidOnResultFragment.startForResult(intent, requestCode, callback);
    }

    public void startForResult(Class<?> clazz, int requestCode, Callback callback) {
        Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
        startForResult(intent, requestCode, callback);
    }

    public interface Callback {
        void onActivityResult(int requestCode, int resultCode, Intent data);
    }
} 

public class AvoidOnResultFragment extends Fragment {
private Map<Integer, PublishSubject> mSubjects = new HashMap<>();
private Map<Integer, AvoidOnResult.Callback> mCallbacks = new HashMap<>();

public AvoidOnResultFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

public Observable<ActivityResultInfo> startForResult(final Intent intent, final int requestCode) {
    PublishSubject<ActivityResultInfo> subject = PublishSubject.create();
    mSubjects.put(requestCode, subject);
    return subject.doOnSubscribe(new Consumer<Disposable>() {
        @Override
        public void accept(Disposable disposable) throws Exception {
            startActivityForResult(intent, requestCode);
        }
    });
}

public void startForResult(Intent intent, int requestCode, AvoidOnResult.Callback callback) {
    mCallbacks.put(requestCode, callback);
    startActivityForResult(intent, requestCode);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //rxjava方式的处理
    PublishSubject<ActivityResultInfo> subject = mSubjects.remove(requestCode);
    if (subject != null) {
        subject.onNext(new ActivityResultInfo(requestCode, resultCode, data));
        subject.onComplete();
    }

    //callback方式的处理
    AvoidOnResult.Callback callback = mCallbacks.remove(requestCode);
    if (callback != null) {
        callback.onActivityResult(requestCode, resultCode, data);
    }
}

}


### AvoidOnResult

先看AvoidOnResult,和RxPermissions一样,也持有一个无视图的fragment,在构造器中先去获取这个AvoidOnResultFragment,getAvoidOnResultFragment、findAvoidOnResultFragment这两个方法是从RxPermissions扒来的,大体就是先通过TAG获取fragment,如果是null就新建一个并add进去。

这个类内部也定义了一个Callback接口,不必多说。

继续,这个类有多个startForResult方法,主要看public void startForResult(Intent intent, int requestCode, Callback callback),它本身不干实事,只是调用fragment的同名方法,所有的逻辑都是fragment中处理,待会儿我们来看这个“同名方法”。

### AvoidOnResultFragment

再看这个fragment,它持有一个mCallbacks,存着requestCode和callback的对应关系。然后找到上边说的同名方法startForResult,只有两行代码,1、把callback存起来,2、调用fragment的startActivityForResult

继续看fragment的onActivityResult方法,主要看注释有“callback方式的处理”的代码,就是从mCallbacks中拿到requestCode对应的callback,调用callback的onActivityResult方法。总体的就是这样了,是不是很简单。

### 拓展

可以看到除了返回值为void的startForResult方法外,还有几个返回值为Observable的,原理一样,只不过fragment中不再是存callback,而是存subject,然后通过doOnSubscribe使它在subscribe的时候跳转页面,最后把得到的Observable返回。对应的,在onActivityResult中拿到对应的subject,通过onNext把数据发出去。

使用示例:

//callback方式
callback.setOnClickListener {
AvoidOnResult(this).startForResult(FetchDataActivity::class.java, REQUEST_CODE_CALLBACK, object : AvoidOnResult.Callback {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) =
if (resultCode == Activity.RESULT_OK) {
val text = data?.getStringExtra(“text”)
Toast.makeText(this@MainActivity, "callback -> " + text, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, “callback canceled”, Toast.LENGTH_SHORT).show()
}

})

}

//rxjava方式
rxjava.setOnClickListener {
AvoidOnResult(this)
.startForResult(FetchDataActivity::class.java, REQUEST_CODE_RXJAVA)
//下面可*变换
.filter { it.resultCode == Activity.RESULT_OK }
.flatMap {
val text = it.data.getStringExtra(“text”)
Observable.fromIterable(text.asIterable())
}
.subscribe({
Log.d("-------> ", it.toString())
}, {
Toast.makeText(this, “error”, Toast.LENGTH_SHORT).show()
}, {
Toast.makeText(this, “complete”, Toast.LENGTH_SHORT).show()
})
}


[AvoidOnResult项目地址](https://github.com/AnotherJack/AvoidOnResult)

所有的工具类都是用java写的,避免使用kotlin编写,出现java无法调用kotlin的情况。测试代码用的kotlin,不过没用太多kotlin的特性,所以即便没接触过kotlin的应该也很好看懂吧!

最后祝大家新年快乐!要是能赏几个star就更好啦!

![](https://www.icode9.com/i/ll/?i=img_convert/0bcb171d8a62277705da04904e5ac384.png)

  

最后

按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。

整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

详细文档可以点我下载,记得点赞哦~

(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

(更新,已反射hook到onActivityResult)如何避免使用onActivityResult,以提高代码可读性

好啦,这份资料就给大家介绍到这了,*有需要详细文档的小伙伴可以点我下载~~~~*

上一篇:Android的Intent


下一篇:startActivityForResult的坑