放弃 startActivityForResult,Activity Result API 优雅使用

放弃 startActivityForResult,Activity Result API 优雅使用

Activity Result API 是 androidx 中的一个新 api,旨在替代原有的 startActivityForResult 方法,用于在两个 Activity 或 Fragment 交换数据、获取返回结果。

过去如果 Activity A 想获取 Activity B 的结果时,需要调用 startActivityForResult 启动 Activity 并重写 onActivityResult 方法获取返回值,这种写法让数据返回的处理逻辑和 Activity 强耦合在一起,代码复用不方便,可读性也很低。

//1.启动Activity
startActivityForResult(intent, 111)

//2.接收返回值:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 111) {
        when (resultCode) {
            1 ->  { ... }
            2 ->  { ... }
        }
    }
}

目前 startActivityForResult 方式已被标为废弃方法,Google 推荐使用 Activity Result API 来进行 Activity 或 Fragment 数据交换,这一改变主要是为了简化代码结构,提高代码的重用性和可维护性。以下是对Activity Result API的详细介绍:

Activity Result API的核心组件

  • ActivityResultContract<I, O>:协议,定义了数据类型、如何传递数据和处理返回数据。ActivityResultContract 是一个抽象类,可以继承它来创建自己的协议。泛型 I 是输入类型,O 是输出类型,如果不需要任何输出可以应用 Unit。

  • ActivityResultCallback<O>:返回结果监听,通过该回调获取返回结果。

  • registerForActivityResult ,启动器注册方法,定义在 ComponentActivity 和 Fragment 中,注册协议并返回启动器。

    该方法需要注意调用位置,调用过晚会抛出异常:

    • Activity:LifecycleOwners must call register before they are STARTED.
    • Fragment:Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate())."
  • ActivityResultLauncher:启动器,调用它的 launch 方法来启动页面跳转,作用相当于原来的 startActivity()。

ActivityResultContract的类型

系统提供了几种常用的 ActivityResultContract 类型,如无特殊需求可以直接使用:

  • StartActivityForResult:通用 Contract,内部未做任何转换。输入 Intent,输出 ActivityResult,是最常用的。

    class StartActivityForResult : ActivityResultContract<Intent, ActivityResult>() {
        override fun createIntent(context: Context, input: Intent):Intent = input
        override fun parseResult(resultCode: Int,intent: Intent?):ActivityResult = ActivityResult(resultCode, intent)
    }
    
  • RequestPermission:请求单个权限。输入类型 String,具体权限;输出类型 Boolean,权限申请结果。

  • RequestMultiplePermissions:请求一组权限。输入类型 Array,权限组;输出类型 Map<String, Boolean> 权限申请结果。

  • TakePicture: 调用 MediaStore.ACTION_IMAGE_CAPTURE 拍照,输入类型 Uri,照片存储地址;输出类型 Boolean 拍照结果。

  • TakePicturePreview:调用 MediaStore.ACTION_IMAGE_CAPTURE 拍照,输入类型 Void? ,输出类型 Bitmap,图片。

  • TakeVideo:调用 MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,输入类型 Uri,视频存储地址;输出类型 Bitmap?,缩略图。

  • PickContact:选择联系人。

  • GetContent:获取用户内容,输入类型 String,过滤的mime 类型(如image/*);输出类型 Uri,内容位置。

  • CreateDocument:创建文档,输入类型 String,新文件的建议名称;输出类型 Uri。

  • OpenDocument:选择单个文档,输入类型 Array,过滤的mime类型;输出类型 Uri。

  • OpenMultipleDocuments:选择多个文档,输入类型 Array, 过滤的mime类型;输出类型 List。

  • OpenDocumentTree:选择一个目录,输入类型 Uri?,可选的初始目录;输出类型 Uri?。

使用步骤

举个例子,FirstActivity -> SecondActivity,需要 FirstActivity 将数据传递给 SecondActivity,SecondActivity 处理完数据后将数据回传给 FirstActivity。

  1. FirstActivity 中需要 调用 registerForActivityResult 方法注册启动器,参数一是启动协议,这里直接用内置的 StartActivityForResult;参数二是数据回调监听,在此处理数据返回逻辑。

  2. 使用启动器的 launch 方法进行页面跳转,Activity A 中代码如下:

    class FirstActivity : AppCompatActivity() {
        lateinit var button: Button
    
        //1.注册启动器
        private val launcher: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), object: ActivityResultCallback<ActivityResult> {
            override fun onActivityResult(result: ActivityResult) {
                //3.处理SecondActivity返回数据
                if (result.resultCode == RESULT_OK) {
                    val content = result.data?.getStringExtra("RESULT_CONTENT")
                    Log.d("baize_", "FirstActivity get result content:${content}}")
                }
            }
        })
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.first_activity)
            button = findViewById(R.id.button)
            button.setOnClickListener {
                //2.启动Activity B,并传递数据
                val intent = Intent(this, SecondActivity::class.java).apply {
                    putExtra("INPUT_CONTENT", "custom data...")
                }
                launcher.launch(intent)
            }
        }
    }
    
  3. SecondActivity 中按传统方式接收和返回数据:

    class SecondActivity : AppCompatActivity() {
        lateinit var button: Button
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_second)
            button = findViewById(R.id.button)
            //1.获取FirstActivity传递来的数据
            val content = intent.getStringExtra("INPUT_CONTENT")
            Log.d("baize_", "SecondActivity get input content:${content}}")
    
            button.setOnClickListener {
                //2.设置返回数据
                val intent = Intent().apply {
                    putExtra("RESULT_CONTENT", editText.text.toString())
                }
                setResult(RESULT_OK, intent)
                finish()
            }
        }
    }
    

自定义协议

继续上面的例子,如果不希望每次跳转都书写同样的 Intent 解析代码,就可以自定义启动器将模板代码封装到里面:

自定义 Contract:

class CustomContract: ActivityResultContract<String, String?>() {
    override fun createIntent(context: Context, input: String): Intent {
        val intent = Intent(context, SecondActivity::class.java)
        intent.putExtra("input", input)
        return intent
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return if (resultCode == RESULT_OK) { intent?.getStringExtra("RESULT_CONTENT") } else null
    }
}

使用时:

//1.注册启动器
private val launcher: ActivityResultLauncher<String> = registerForActivityResult(CustomContract(), object: ActivityResultCallback<String?> {
    override fun onActivityResult(result: String?) {
        //3.直接处理返回值
        Log.d("baize_", "activity 1 get result content:${result}}")
    }
})

//2.启动页面,直接传入String值
launcher.launch("custom data...")

最后效果:

总结

使用 Activity Result API 相比传统的 startActivityForResult()onActivityResult() 方法,具有以下优点:

  • 代码更加简洁:通过将结果处理逻辑与 Activity 的生命周期解耦,使得代码更加简洁易读。
  • 提高重用性:自定义的 ActivityResultContract 可以在多个地方重复使用,提高代码的重用性。
  • 易于维护:减少了 onActivityResult 方法的嵌套和耦合,使得代码更加易于维护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值