1. Introduction

When I used the startActivityForResult() API these days, I suddenly noticed that it was deprecated.

Through the annotations of the source code, I found that the official has given a new solution.

2. Shortcomings of the original plan

When we use onActivityResult, there are often many different return cases. In this case, we need to set different return codes, and then judge in onActivityResult. Too many return codes will reduce the readability of the code, but also improve the coupling degree.

3.ActivityResult API is simple to use

First import the required dependencies.

Implementation 'androidx. Activity: the activity: 1.3.0' implementation 'androidx. Fragments: fragments: 1.3.4'Copy the code

Next, you need to create a class to integrate ActivityResultContract<I,O>, where I is the data type carried by the intent jump and O is the data type carried by the intent return. You also need to implement the createIntent and parseResult methods, one to create the intent and one to parse the data returned by the 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 } }Copy the code

Once it’s created, we can use it in our Activity.

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

Then just call ActivityResultContract where to jump. The launch method. After writing this whole step, you can see that the coupling is reduced, but the whole thing becomes more and more cumbersome, and you have to create a new class, which feels like the onActivityResult API. Fortunately, the official ActivityResulyContract has been provided with some defined ActivityResulyContract, which can meet most of our needs.

A. Contract B. Contract C. Contract D. Contract

Contract role
StartActivityForResult Contract, I–>Intent, O –> ActivityResult
RequestMultiplePermissions Multi-permission request, I- >Array, O- >Map<String,Boolean>, enter the permission name to request, output the permission name and whether the Map
TakePicturePreview Call mediastore.action_image_capture to take a picture and return a Bitmap
TakePicture Call mediastore.action_image_capture to take a photo, save it to the given Uri address, and return a Boolean
TakeVideo Call mediastore. ACTION_VIDEO_CAPTURE to capture the video, save it to the specified Uri, and return a thumbnail
PickContact Select a contact from the Contacts APP and return a URI
GetContent Passing in a specific MIME type opens the corresponding file manager
CreateDocument Prompts the user to create a file with I as the file name
CreateMultipleDocuments The user is prompted to select multiple documents. I indicates the file type
OpenDocumentTree Prompts the user to select a directory and returns the Uri of the user’s choice

Demo

package com.zjl.activityresultdemo import 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) } } }Copy the code

5. Receive results in non-activity /Fragment

We can receive results between an Activity and a Fragment because their base classes implement an ActivityResultCaller. Sometimes we need to receive results in a component that is not an Activity or Fragment. This can be done using ActivityResultRegistry.

The 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/*")
    }
​
}
Copy the code

References:

1. Goodbye! OnActivityResult! Hello, Activity Results API!