专栏首页Android原创优雅的监听onActivityResult
原创

优雅的监听onActivityResult

一、思考

当Activity A启动一个Activity B后,B finish,我们可能需要处理B返回的值,此时,我们可能就需要用到了onActivityResult,需要在Activity A中重写实现。 当然,在这种前提是我们处理逻辑都是放在Activity A类中,那么假如我们是在一个其他类,不是Activity或者Fragment的情况呢?

image.png

此时,我们可能会用到EventBus这种全局分发事件的方式来处理,但种感觉不够优雅。

当我看到Google的开源的lifecycle库中的HolderFragment的做法,感觉眼前一亮。在Activity或者Fragment中添加一个空壳的Fragment,作为一个生命周期的监听。

同样的,开源库Glide也有相似的设计。

image.png

这种方法挺好的。

所以我产生以下思路:通过给当前的Activity添加一个空壳的Fragment,往空壳的Fragment中传入Listener,然后由该Fragment去启动新的Activity,最终在这个空壳的FragmentonActivityResult中将结果传到Listener返回出去。

二、撸码

1、 首先,我们对外暴露一个Fragment需要实现的接口,让外部使用这个接口,而不是直接使用这个Fragment,做接口隔离。

public interface ListenActivityResultRequest {
    void startActivity(Intent intent, int requestCode, OnActivityResultCallBack callBack);

    interface OnActivityResultCallBack {
        void onActivityResult(int resultCode, Intent data);
    }
}

该接口的方法提供一个startActivity,让外部传入intentrequestCode,还有处理onActivityReuslt的回调OnActivityResultCallBack

2、空壳Fragment的实现

(1)创建空壳Fragment

    private static ListenActivityResultFragment createHolderFragment(FragmentManager fragmentManager) {
        ListenActivityResultFragment holder = new ListenActivityResultFragment();
        fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitNowAllowingStateLoss(); // need quickly
        return holder;
    }

(2)可能会已经存在有一个空壳Fragment,所以需要加一个查找的逻辑

    private static ListenActivityResultFragment findHolderFragment(FragmentManager manager) {
        if (manager.isDestroyed()) {
            throw new IllegalStateException("Can't access FragmentManager from onDestroy");
        }

        Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
        if (fragmentByTag != null && !(fragmentByTag instanceof ListenActivityResultFragment)) {
            throw new IllegalStateException("Unexpected fragment instance was returned by HOLDER_TAG");
        }
        return (ListenActivityResultFragment) fragmentByTag;
    }

所以最终调用以上两个方法的方式是:

    public static ListenActivityResultRequest holderFragmentFor(FragmentActivity activity) {
        return holderFragmentFor(activity.getSupportFragmentManager());
    }

    public static ListenActivityResultRequest holderFragmentFor(Fragment fragment) {
        return holderFragmentFor(fragment.getChildFragmentManager());
    }

    private static ListenActivityResultFragment holderFragmentFor(FragmentManager fm) {
        ListenActivityResultFragment holder = findHolderFragment(fm);
        if (holder == null) {
            holder = createHolderFragment(fm);
        }
        return holder;
    }

支持ActivityFragment两种情况,先执行查询,找不到再创建空壳Fragment

(3)实现ListenActivityResultRequest接口

    private SparseArray<OnActivityResultCallBack> mCallBackMap = new SparseArray<>();

    @Override
    public void startActivity(Intent intent, int requestCode, OnActivityResultCallBack callBack) {
        mCallBackMap.put(requestCode, callBack);
        startActivityForResult(intent, requestCode);
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        OnActivityResultCallBack callBack = mCallBackMap.get(requestCode);
        if (callBack != null) {
            callBack.onActivityResult(resultCode, data);
        }
    }

这里使用一个字典SparseArray保存requestCode对应的OnActivityResultCallBack,根据实际需求情况,可以任意更改,这里只是举个栗子。

3、暴露给外提供的类

public class ListenActivityProviders {
    public static ListenActivityResultRequest listenActivityResult(FragmentActivity activity) {
        return ListenActivityResultFragment.holderFragmentFor(activity);
    }

    public static ListenActivityResultRequest listenActivityResult(Fragment fragment) {
        return ListenActivityResultFragment.holderFragmentFor(fragment);
    }
}

名字随便取的,见谅见谅~

三、最终使用方式

int requestCode = 1;
Intent intent = new Intent(context, XXActivity.class);
ListenActivityProviders.listenActivityResult(activity).startActivity(intent, requestCode, new OnActivityResultCallBack() {
	public void onActivityResult(int resultCode, Intent data) {
	// to handle result....
	}
});
image.png

四、结合Rxjava

可能需求逻辑会比较复杂,可能需要连续的处理,为了避免各种异步接口带了的“回调地狱”,我们可能可以在这里将接口改成RxjavaObservable

在以上实现的基础上,在ListenActivityResultRequest增加一个返回Observable的方法。

Observable<ActivityResult> startActivity(Intent intent, int requestCode);

class ActivityResult {
    public final int resultCode;
    public final Intent data;
    ActivityResult(int resultCode, Intent data) {
        this.resultCode = resultCode;
        this.data = data;
    }
}

然后在空壳Fragment中实现:

@Override
public Observable<ActivityResult> startActivity(Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode);
    return Observable.create(emitter -> mCallBackMap.put(requestCode, new OnActivityResultCallBack() {
        @Override
        public void onActivityResult(int resultCode, Intent data) {
            emitter.onNext(new ActivityResult(resultCode, data));
            emitter.onComplete();
        }
    }));
}

五、拓展

同理,对于Android处理运行时权限,也可同样适用这样的思路。

有个开源库有这么实现

最终使用的方法:

Activity that = this; // or fragment

String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};  // permissions

KPermission.of(that).requestPermissions(permissions, new IPermissionRequest.IPermissionCallback() { // request 
    @Override
    public void onResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // callback
        StringBuilder stringBuilder = new StringBuilder("onResult\n");
        for (int i = 0; i < permissions.length; i++) {
            stringBuilder.append(permissions[i].replaceFirst("android.permission.", ""))
                    .append(" is ")
                    .append(grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied");
        }
        Log.d("KPermission", stringBuilder.toString());
        Toast.makeText(getApplicationContext(), stringBuilder.toString(), Toast.LENGTH_SHORT).show();
    }
});
image.png

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android OpenGL ES滤镜开发设计

    按照正常的Android OpenGL开发,一般只需引入两个“主角”:GLSurfaceView和Renderer。

    Clayman Twinkle
  • 在Android中显示APNG动图

    APNG(Animated Portable Network Graphics)是一个基于PNG(Portable Network Graphics)的位图动画...

    Clayman Twinkle
  • 现有项目接入Kotlin开发实战

    众所周知,Google强力推出了Kotlin作为Android开发的第一语言,那么我们现有用Java语言开发的项目,如何去接入Kotlin开发呢?

    Clayman Twinkle
  • 【干货】学习编程的正确打开方式!

    这个问题是老九君最近几年来最关注的点。老九君觉得网上提及的许多方法尽管都很不错,然而老九君却注意到,成功的童鞋,无论使用哪种方法,往往都会在以下三个方面,比其他...

    老九君
  • MySQL中用快速为select结果加序号

    set @a=0;select @a:=@a+1,user,host from mysql.user;

    莫晓东do
  • 并发编程之信号量

    爱撒谎的男孩
  • django中CBV

    当请求过来的时候, 会优先判断你的请求方法是GET还是POST, 如果是GET请求的话, 走GET函数, 反之, 走POSt函数

    小小咸鱼YwY
  • 如何面试刚毕业的开发者?这位面试官总结了一些心得

    大数据文摘
  • 深入理解JavaScript系列(44):设计模式之桥接模式

    上述代码,有个问题就是getBeerById必须要有浏览器的上下文才能使用,因为其内部使用了this.id这个属性,如果没用上下文,那就歇菜了。所以说一般稍微有...

    用户4962466
  • Django之CURD插件

    CURD顾名思义就是create,update,rearch,delete(所谓的增删改查).

    菲宇

扫码关注云+社区

领取腾讯云代金券