Activity Result API学习

1,528 阅读3分钟

1.前言

这两天使用startActivityForResult()这个API的时候,突然发现它被废弃了。

image-20210803104135839

通过源代码的注解我发现官方已经给了新的解决方案。

2.原有方案的缺点

我们在使用onActivityResult的时候,往往会有多种不同返回的情况,这种时候我们就需要设定不同的返回码,然后再onActivityResult中进行判断,过多的返回码会降低代码的可读性,还提升了耦合度。

3.ActivityResult API简单使用

首先要导入需要的依赖。

    implementation 'androidx.activity:activity:1.3.0'
    implementation 'androidx.fragment:fragment:1.3.4'

其次需要创建一个类来集成ActivityResultContract<I,O>,这里的I是intent跳转时携带的数据类型,O是intent返回时的携带的数据类型。还需要实现createIntent和parseResult这两个方法,这两个方法一个是用来创建intent的,一个是用来解析intent返回的数据。

class MyActivityResultContract : ActivityResultContract<String, String>() {
    override fun createIntent(context: Context, input: String?): Intent {
        return Intent(context,SecondActivity::class.java).apply {
            putExtra("name",input)
        }
    }
​
    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        val data = intent?.getStringExtra("result")
        return if (resultCode == Activity.RESULT_OK && data != null) data else null
    }
}

创建完成之后我们就可以在Activity中使用它。

private val myActivityResultContract = registerForActivityResult(MyActivityResultContract()){ result ->
        Toast.makeText(applicationContext, result, Toast.LENGTH_SHORT).show()
        textView.text = result
    }

接下来只要在需要跳转的地方调用ActivityResultContract.launch方法即可。写完这一整个步骤,我们可以看到,耦合度虽然降低了,但是整件事情变得越来越麻烦了,还要新建一个类,感觉这样还不如onActivityResult API了。还好官方已经给出了一些定义好的ActivityResulyContract,这些已经能满足我们大部分需求。

4.常用Contract的介绍

Contract作用
StartActivityForResult通用的Contract,I-->Intent,O->ActivityResult
RequestMultiplePermissions多权限请求,I-->Array,O-->Map<String,Boolean>,输入的是要请求的权限名,输出权限名和是否成功组成的Map
TakePicturePreview调用MediaStore.ACTION_IMAGE_CAPTURE来拍照,返回值为Bitmap
TakePicture调用MediaStore.ACTION_IMAGE_CAPTURE来拍照,保存到给定Uri地址,返回一个Boolean
TakeVideo调用MediaStore.ACTION_VIDEO_CAPTURE拍摄视频,保存给指定Uri地址,返回一张缩略图
PickContact从通讯录APP选中联系人,返回一个uri
GetContent传入特定的mime type打开对应的文件管理器
CreateDocument提示用户创建一个文件,I为文件名
CreateMultipleDocuments提示用户选择多个文档,I为文件类型
OpenDocumentTree提示用户选择一个目录,并返回用户选择的Uri

Demo

package com.zjl.activityresultdemoimport android.Manifest
import android.content.ContentValues
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
​
class MainActivity : AppCompatActivity() {
​
    private val myActivityResultContract =
        registerForActivityResult(MyActivityResultContract()) { result ->
            Toast.makeText(applicationContext, result, Toast.LENGTH_SHORT).show()
        }
​
    private val mPermissionResultContract =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
            Toast.makeText(applicationContext, "${it.keys}...${it.values}", Toast.LENGTH_SHORT)
                .show()
        }
​
    private val mTakePictureContract =
        registerForActivityResult(ActivityResultContracts.TakePicture()) {
            Toast.makeText(applicationContext, "$it", Toast.LENGTH_SHORT).show()
        }
​
    private val mTakePicturePreviewContract =
        registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
            iv.setImageBitmap(it)
        }
​
    private val mTakeVideoContract =
        registerForActivityResult(ActivityResultContracts.TakeVideo()) {
            iv.setImageBitmap(it)
        }
​
    private val mPicContact =
        registerForActivityResult(ActivityResultContracts.PickContact()) {
            Log.e("Testtt",it.toString())
        }
​
    private val mGetContact =
        registerForActivityResult(ActivityResultContracts.GetContent()) {
            Log.e("Testtt",it.toString())
        }
​
    private val mCreateDoc =
        registerForActivityResult(ActivityResultContracts.CreateDocument()) {
​
        }
​
    private val mOpenMultipleDoc =
        registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) {
​
        }
​
    private val mOpenDocTree =
        registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
​
        }
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_permission.setOnClickListener {
            //myActivityResultContract.launch("test")
            mPermissionResultContract.launch(
                arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                )
            )
        }
​
        btn_take_pic.setOnClickListener {
            contentResolver.insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                ContentValues()
            ).let {
                mTakePictureContract.launch(it)
            }
        }
​
        btn_take_pic_preview.setOnClickListener {
            mTakePicturePreviewContract.launch(null)
        }
​
        btn_take_video.setOnClickListener {
            contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, ContentValues())
                .let {
                    mTakeVideoContract.launch(it)
                }
        }
​
        btn_pic_contact.setOnClickListener {
            mPicContact.launch(null)
        }
​
        btn_get_contact.setOnClickListener {
            mGetContact.launch("video/*") //mime type
        }
​
        btn_create_doc.setOnClickListener {
            mCreateDoc.launch("image/*")
        }
​
        btn_open_multi_Doc.setOnClickListener {
            mOpenMultipleDoc.launch(arrayOf("image/*","video/*"))
        }
​
        btn_open_doc_tree.setOnClickListener {
            mOpenDocTree.launch(MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        }
    }
}

5.在非Activity/Fragment中接收结果

我们在Activity和Fragment中可以之间接收到返回结果是因为它们的基类都实现了ActivityResultCaller。但有时候我们需要在非Activity非Fragment的组件中接收返回的结果,这时候我们就可以使用ActivityResultRegistry来实现。

Demo:

class SecondActivity : AppCompatActivity() {
​
    private lateinit var observer: MyLifeCycleObserver
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        observer = MyLifeCycleObserver(activityResultRegistry)
        lifecycle.addObserver(observer)
​
        btn2.setOnClickListener { observer.selectImage() }
    }
​
​
}
​
class MyLifeCycleObserver(private val registry: ActivityResultRegistry) : LifecycleObserver{
​
    private lateinit var getContent: ActivityResultLauncher<String>
​
​
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreate(owner: LifecycleOwner) {
        getContent = registry.register("key",owner,ActivityResultContracts.GetContent()) {
            Log.e("TTTEE",it.toString())
        }
    }
​
    fun selectImage() {
        getContent.launch("image/*")
    }
​
}

参考资料:

1.再见!onActivityResult!你好,Activity Results API!