写点什么

是时候丢掉 onActivityResult 了 !

用户头像
Android架构
关注
发布于: 5 小时前

}


private fun jump() {


startActivityForResult(Intent(this, SecondActivity::class.java), REQUEST_CODE)


}


override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {


super.onActivityResult(requestCode, resultCode, data)


if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE) {


toast(data?.getStringExtra("value") ?: "")


}


}API


}


  • 定义一个 REQUEST_CODE ,同一页面有多个时,保证不重复

  • 调用 startActivityForResult

  • 在 onActivityResult 中接收回调,并判断 requestCode,resultCode


上面的逻辑中不乏重复的样板代码,且大多都耦合在视图控制器(Activity/Fragment)中,也就造成了不易测试。细品一下,的确不是那么的合理。


可能一直以来我们也只有这一个选择,所以也很少看到有人抱怨 onActivityResult。精益求精的 Google 工程师为我们改进了这一问题。


下面来看看如何使用最新的 Activity Result API 。


Activity Result API




private val startActivity =


prepareCall(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult? ->


toast(result?.data?.getStringExtra("value") ?: "")


}


override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)


jump.setOnClickListener { jump() }


}


private fun jump() {


startActivity.launch(Intent(this,SecondActivity::class.java))


}


恩,就是这么简单。主要就两个方法,prepareCall()launch()。拆解开来逐一分析。


public <I, O> ActivityResultLauncher<I> prepareCall(


@NonNull ActivityResultContract<I, O> contract,


@NonNull ActivityResultCallback<O> callback) {


return prepareCall(contraguanxict, mActivityResultRegistry, callback);


}


prepare() 方法接收两个参数,ActivityResultContractActivityResultCallback ,返回值是 ActivityResultLauncher。这几个名字取得都很好,见名知意。

ActivityResultContract

ActivityResultContract 可以理解为一种协议,它是一个抽象类,提供了两个能力,`createIn


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


tentparseResult` 。这两个能力放到启动 Activity 中就很好理解了,createIntent 负责为 startActivityForResult 提供 Intent ,parseResult 负责处理 onActivityResult 中获取的结果。


上面的例子中,prepare() 方法传入的协议实现类是 StartActivityForResult。它是 ActivityResultContracts 类中的静态内部类。除了 StartActivityForResult 之外,官方还默认提供了 RequestPermissionsDialRequestPermissionTakePicture ,它们都是 ActivityResultContract 的实现类。


所以,除了可以简化 startActivityForResult,权限请求,拨打电话,拍照,都可以通过 Activity Result API 得到了简化。除了使用官方默认提供的这些之外,我们还可以自己实现 ActivityResultContract,在后面的代码中会进行演示。

ActivityResultCallback

public interface ActivityResultCallback<O> {


/**


  • Called when result is available


*/


void onActivityResult(@SuppressLint("UnknownNullness") O result);


}


这个就比较简单了。当回调结果可用时,通过该接口通知。需要注意的一点是,由于 prepare() 方法的泛型限制,这里的返回值 result 一定是类型安全的。下表是系统内置协议和其返回值类型的对应关系。


Github



ActivityResultLauncher


prepare()方法的返回值。


prepare()方法其实会调用 ActivityResultRegistry.registerActivityResultCallback()方法,具体的源码这里就不分析了,后面会单独写一篇源码解析。大致流程就是,自动生成 requestCode,注册回调并存储起来,绑定生命周期,当收到Lifecycle.Event.ON_DESTROY 事件时,自动解绑注册。


@Override


public <I, O> void invoke(


final int requestCode,


@NonNull ActivityResultContract<I, O> contract,


I input) {


Intent intent = contract.createIntent(input);


if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) {


// handle request permissions


} else {


ComponentActivity.this.startActivityForResult(intent, requestCode);


}


}


中间那一块处理 request permissions 的我给掐掉了。这样看起来看清晰。本来准备单独水一篇源码解析的,这马上核心源码都讲完了。


前面展示过了 startActivityForResult(),再来展示一下权限请求。


private val requestPermission = prepareCall(ActivityResultContracts.RequestPermission()){


result -> toast("request permission $result")


}


requestPermission.launch(Manifest.permission.READ_PHONE_STATE)


拨打电话,拍照就不在这里展示了。所有的示例代码都已经上传到了我的 Github 。


如何自定义返回值 ?




前面提到的都是系统预置的协议,返回值也都是固定的。那么,如何返回自定义类型的值呢?其实也很简单,自定义 ActivityResultContract就可以了。


我们以TakePicture为例,默认的返回值是Bitmap ,现在我们让它返回 Drawable


private class TakePicDrawable : ActivityResultContract<Void,Drawable>(){


override fun createIntent(input: Void?): Intent {


return Intent(MediaStore.ACTION_IMAGE_CAPTURE)


}


override fun parseResult(resultCode: Int, intent: Intent?): Drawable? {


if (resultCode != Activity.RESULT_OK || intent == null) return null


val bitmap = intent.getParcelableExtra<Bitmap>("data")


return BitmapDrawable(bitmap)


}


}


使用:


private val takePictureCustom = prepareCall(TakePicDrawable()) { result ->


toast("take picture : $result")


}


pictureCustomBt.setOnClickListener { takePictureCustom()}


这样就可以调用系统相机拍照并在结果回调中拿到 Drawable 对象了。


说好的解耦呢 ?




有时候我们可能会在结果回调中进行一些复杂的处理操作,无论是之前的onActivityResult()还是上面的写法,都是直接耦合在视图控制器中的。通过新的 Activity Result API,我们还可以单独的类中处理结果回调,真正做到 单一职责 。


其实 Activity Result API 的核心操作都是通过 ActivityResultRegistry 来完成的,ComponentActivity 中包含了一个 ActivityResultRegistry对象 :


@NonNull


public ActivityResultRegistry getActivityResultRegistry() {


return mActivityResultRegistry;


}


现在要脱离 Activity 完成操作,就需要外部提供一个 ActivityResultRegistry对象来进行结果回调的注册工作。同时,我们一般通过实现 LifecycleObserver 接口,绑定一个 LifecycleOwner来进行自动解绑注册。完整代码如下:


class TakePhotoObserver(


private val registry: ActivityResultRegistry,


private val func: (Bitmap) -> Unit


) : DefaultLifecycleObserver {


private lateinit var takePhotoLauncher: ActivityResultLauncher<Void?>


override fun onCreate(owner: LifecycleOwner) {


takePhotoLauncher = registry.registerActivityResultCallback(


"key",


ActivityResultContracts.TakePicture()


) { bitmap ->


func(bitmap)


}


}


fun takePicture(){


takePhotoLauncher()

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
是时候丢掉 onActivityResult 了 !