Blog
>
Technology

ActivityResultContract, ActivityResultAPIs in AndroidX

Nitesh Goyal
I
August 2, 2021
I

I was going through the ActivityResultAPIs introduced in AndroidX and found them very useful so I decided to share my knowledge here.

Use case: The use case is pretty simple, Start a new activity and get some result back from the newly started activity. I know what you are thinking….. startActivityForResult() and onActivityResult() right. You are absolutely right that we have been using these methods for this use case.

Android has introduced ActivityResultAPIs to handle the same use case in a better and easy way plus you get a lot of other features, will discuss these features later :).

Why should one use it: Many of us will have this question that why should we use this API when we already have startActivityForResult(), yes? Let me give you one use case where you may find this API extremely useful.

When starting an activity for a result, it is possible (and, in cases of memory-intensive operations such as camera usage, almost certain) that your process and your activity will be destroyed due to low memory.”

For this reason, the Activity Result APIs decouples the result callback from the place in your code where you launch the other activity. Since the result callback needs to be available when your process and activity are recreated, the callback must be unconditionally registered every time your activity is created, even if the logic of launching the other activity only happens based on user input or other business logic. (You will see this in the code)” source: developer.android.com

Solution: Let’s discuss the solution to the above-mentioned problem where our process may get killed and we may lose the result.


dependencies {
// It is still in alpha so it may change in future
implementation 'androidx.activity:activity-ktx:1.2.0-alpha04'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha04'
}
class MainActivity : AppCompatActivity() {
//unconditionally registered every time your activity is created
// Yes this piece of code is not inside any method and written at //class level.
private val capturePhoto = 
registerForActivityResult(ActivityResultContracts.TakePicture() {
              //Callback for result
      bitmap -> // Now this bitmap can be used wherever we want
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    btn_capture.setOnClickListener {
      capturePhoto.invoke(null)
    }
}
}

Let’s discuss each component that we have used in the above code. There are mainly three components while using ActivityResultAPI. Contract, ActivityResultLauncher, ResultCallback.

  1. Contract:

A Contract defines

An intent which will be used to start an activity and

Receives the result intent from the started activity and parses it.

An activity can be called with an input of type I and produce an output of type O.

In the above example ActivityResultContracts.TakePicture() is a pre built contract provided by Android.

2. ActivityResultLauncher: A launcher that is used to start the process of executing an ActivityResultContract. capturePhoto is nothing but our ActivityResultLauncher which is returned by registerForActivityResult(). In simple words, if we have to start an activity for result using ActivityResultAPI then we will simply use ActivityResultLauncher like capturePhoto.invoke(). This will launch ActivityResultContract which in turn launch the intent to startTheActivity by itself.

3. registerForActivityResult: is used to register a contract and this is the place where you get the callback for the result, in the above example callback is passed as lambda. This method returns an object of ActivityResultLauncher.


public final  ActivityResultLauncher registerForActivityResult(
        @NonNull ActivityResultContract contract,
        @NonNull ActivityResultCallback callback) {
    return registerForActivityResult(contract, mActivityResultRegistry, callback);
}

Custom Contracts

TakePicture() Contract that we used in the earlier example was a pre-built contract which has a predefined Intent with it. We use Custom Contract to start an activity for result from one activity or fragment to another. Yes, you heard it right. We can use this API for fragments also. Noticed fragment-ktx:1.3.0-alpha04?

To define a custom contract we need to create

  1. A class that extends ActivityResultContract class. This is how we decouple the startActivity() and onActivityResult code from the main activity. Let’s look at the code.

//ActivityResultContract() {

    override fun createIntent(context: Context, input: String): Intent {
        //val bundle = Bundle()
        //bundle.putString(key, input)
        val intent = Intent(context, SecondActivity::class.java) 
        //intent.putExtra(key, input)
        //intent.putExtra(bundleKey, bundle)
        return intent
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
     // Process the data received from second activity.
            RESULT_OK -> intent?.getStringExtra(DATA)
            else -> null
        }
    }
}

2. Register for activity result.


private val fetchDataFromSecondActivity = registerForActivityResult(SecondActivityContract()) { data ->
    // data is the processed result from SecondActivityContract
}

3. Use the launcher returned by registerForActivityResult which is fetchDataFromSecondActivity. Let’s say we want to launch the second activity on click of a button.


btn_launch_second_activity.setOnClickListener {
    fetchDataFromSecondActivity.invoke("Test Input")
}

“Test Input” is the input that will be passed to createIntent() of the SecondActivityContract.

As usual in the SecondActivity all we have to do is setResult(Activity.RESULT_OK, intent)

That was all about the Custom Contract.

Pre-build Contracts

Android provides some really useful Contracts which are pre-built by Android itself. To use these contracts you can use ActivityResultContract class.

For e.g. ActivityResultContracts.TakePicture()

All of the pre-build contracts can be found at https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContract

I personally found permission contracts very useful as we do not have to handle boilerplate code to request Android runtime permissions. This is how we can use permission contracts

  1. Get a launcher to trigger the permission request

private val askMultiplePermissions = 
//ActivityResultContracts.RequestPermission() for single permission
registerForActivityResult(ActivityResultContracts.RequestMultiplePer
missions()) {map : MutableMap ->
for (entry in map.entries)
    {
       //entry.key is the permission name and entry.value is a boolean which tells whether permission was granted by the user or not
   }
}

2. Trigger the permission request


btn_permission.setOnClickListener {             
   askMultiplePermissions(arrayOf(android.Manifest.permission.ACCESS_FI NE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE))
}

Start activity for result from fragment

With ActivityResultAPI it is fairly simple and same as did in activity.


class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn_get_data.setOnClickListener {
            fetchDataFromSecondActivity("Calling from fragment")
        }
    }
    private val fetchDataFromSecondActivity = registerForActivityResult(SecondActivityContract()) { data ->
        tv_result.text = data
    }
}

You can find an example on fragment using this API in the following GitHub project
https://github.com/goyalish/ActivityResultApi.

Android Development
Design

About Quinbay

Quinbay is a dynamic one stop technology company driven by the passion to disrupt technology today and define the future.
We private label and create digital future tech platforms for you.

Digitized . Automated . Intelligent