首页 文章详情

如何优雅地在Activity之间传递数据?

Carson带你学习Android | 674 2021-04-30 07:39 0 0 0
UniSMS (合一短信)

背景

针对Activity传递数据,常见方式是:

  • 步骤1:通过Intent存储携带数据;
  • 步骤2:使用startActivityForResult()启动Activity;
  • 步骤3:通过onActivityResult()接收返回结果。

问题

onActivityResult()回调里存在大量的业务回调嵌套,耦合严重。


解决方案

Google官方推荐:「Activity Results API」

注:Google在androidx.activity和fragment的alpha02版本中,已经废弃了startActivityForResult()、onActivityResult()。


简介

Google官方推荐的用于Activity、Fragment获取返回结果的方式。(用于替代onActivityResult())


优点

  • 使用方便
  • 降低了业务之间的耦合
  • 减少了样板代码(如定义大量requestCode常量用于区分业务)

使用介绍

Activity Results API 中有两个核心使用组件:

  • ActivityResultContract
  • ActivityResultLauncher

ActivityResultContract

是一个抽象类,需要继承来创建自己的协议,定义了如何传递数据和如何处理返回的数据。Google预置了大量常用的Contract供开发者直接使用,具体如下:

// 最常用、最通用
StartActivityForResult() 
// 不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定。

// 权限相关
RequestPermission()
// 用于请求单个权限
RequestMultiplePermissions()
// 用于请求一组权限

// 多媒体相关
TakePicturePreview()
// 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片
TakePicture()
// 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将图片保存到给定的Uri地址,返回true表示保存成功。
TakeVideo()
// 调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,保存到给定的Uri地址,返回一张缩略图。

// 文档相关
CreateDocument()
// 提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri。
OpenMultipleDocuments()
// 提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。
OpenDocumentTree()
// 提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。

当然,也可以自定义ActivityResultContract,具体定义步骤如下:

// 步骤1:新建一个Contract类,继承自ActivityResultContract<I,O>
// 参数说明:
// I:输入的类型
// O:输出的类型
class MyActivityResultContract: ActivityResultContract<String,String>(){

// 步骤2:实现createIntent()
// 输入类型I作为createIntent的参数
    override fun createIntent(context: Context, input: String?): Intent {
        return Intent(context,SecondActivity::class.java).apply {
            putExtra("test",input)
        }
    }

    // 步骤3:实现parseResult()
    // 输出类型O作为parseResult方法的返回值
    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        val data = intent?.getStringExtra("result")
        return if (resultCode == Activity.RESULT_OK && data != null) data
        else null
    }
}

在上述例子:

  • 输入输出类型都是String;
  • 在createIntent()通过Intent携带了参数为test的数据;
  • 在parseResult()通过Intent获得了返回的数据result。

ActivityResultLauncher

启动器,通过调用ActivityResultLauncher.launch()来启动页面跳转(作用相当于原来的startActivity())。


使用步骤

  • 步骤1:注册协议Contract(可采用系统内置的 or 开发者自定义)
  • 步骤2:创建启动器ActivityResultLauncher & 设置回调
  • 步骤3:打开新Acitivity时传递数据
  • 步骤4:关闭新Activity时回传数据

使用示例

此处展示的是:

  • 从Activity1打开Activity2时传递数据:carsonho_request
  • 从Activity2关闭回到Activity1时回传数据:carsonho_response

具体使用代码

// 步骤1:注册协议Contract
// 可采用系统内置的 or 开发者自定义
// 此处采用系统内置的StartActivityForResult()展示

// 步骤2:创建启动器ActivityResultLauncher & 设置回调
 private val myActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ activityResult ->  
        if(activityResult.resultCode == Activity.RESULT_OK){
         // 收到回调数据result:carsonho_response
            val result = activityResult.data?.getStringExtra("result")
        }
    }

// 步骤3:从Activity1打开Activity2时传递数据,即从Activity1打开Activity2时
 button.setOnClickListener {
        val  intent = Intent(this,SecondActivity::class.java).apply {
             putExtra("test","carsonho_request")
        }
        myActivityLauncher.launch(intent)
}

// 步骤4:打开新Activity时接收数据,即打开Activity2时
val test = intent.getStringExtra("test")

// 步骤5:关闭新Activity时回传数据,即从Activity2关闭回到Activity1时
button.setOnClickListener {
    val intent = Intent().apply {
        putExtra("result","carsonho_response")
    }
    setResult(Activity.RESULT_OK,intent)
    finish()
}

「Carson每天带你学习一个Android知识点」,长按扫描关注公众号。同时,期待您精彩文章的投稿:真诚邀请您来分享

最后福利:学习资料赠送

  • 福利:由本人亲自撰写 & 整理的「Android学习方法资料」
  • 数量:10名
  • 参与方式:「点击文章右下角”在看“ -> 回复截图到公众号 即可,我将从中随机抽取」

    点击“在看”就能升职 & 加薪水哦!



good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter