Deep Dive into Activity Results API — No More onActivityResult()

ActivityResultContracts by Examples

Since Android came into existence in 2007, Activity has been one of its core components. One of the most common tasks in apps is transferring data between two Activities. Until now, Intents and onActivityResult were the only choice.

Through the combination of these two parts, developers are able to transfer data from one Activity to another and get data back easily.



Let’s elaborate through an example: your app wants to capture an image and display it to the user. You either write your own custom Camera or delegate the task of fetching an image to Android through Intents. In this second scenario, Android system will open the user-preferred Camera app, capture the image and deliver the requested data to your activity by calling onActivityResult() method.

I am working on the video tutorial of this article and will upload it on my YouTube channel soon. Please subscribe to watch that and more Android tutorials like this.


Subscribe to My YouTube Channel


Traditional Way — The onActivityResult() Method

Whether its an image Bitmap from Camera app or its a Image from the gallery, or maybe its some custom result from your some other Activity of app, Android system will call onActivityResult() method in the original requesting Activity or Fragment class.

If you one case of capturing image or multiple cases like picking images from gallery, requesting gazillion permissions, and handling your own custom data with app’s other screens, all the results of these actions are handled in only one method onActivityResult() . And this method will look something like this in your code.

I have a feeling that you don’t like this kind of code to read / write at all. Neither do I. Because this kind of code brings lots of problems and unwanted bugs such as:

  • Tight Coupling: There’s no other place where you can put or abstract this code and business logic. Like if you want to separate each case of Image Capture or Pick Image etc. You can’t do that. You can delegate later, but starting point is this. And this will create a bad cluster like above code with nested if-else blocks.

  • Type-Safety: You may get wrong type of data by minor mistakes. You can get your Integer value as String and later spend hours on debugging why app’s not working the way its supposed to. This is because you are relying on String keys to put/read from Intent data. You will have to make sure that you are using right type for the right key.

  • Unwanted NullPointerException: Who is not annoyed of NullPointerException ? If you get a typo in writing key of to either put or retrieve data in the Intent , you will get the NullPointerException like a bombing crash in your app. This can waste a lot of time as you might debug on why your data is null. How are you supposed to think that you can mistakenly miss some letter in your key at that time?

So, Google brings us a solution which is cleaner in code readability with not much if-else blocks, less coupling and separate places for each case, type safety to make sure the right data gets to right method, and absolutely no NullPointerException because of typos.


Introducing Activity Results API — The New Way

I’m not sure what this will be called — Activity Results API or Activity Result Contracts or both. But surely it sounds nice.

Starting with Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02, you now have a nice abstraction which allows you to handle onActivityResult() method in a very clean and reusable structure.

Let’s see how this new API is used then.


Adding Dependencies

First, you have to add the following dependencies in your app’s build.gradle file.

Please note that at the time of writing this article, latest version is 1.2.0-alpha04 for activity-ktx and 1.3.0-alpha04 for fragment-ktx . You can check the latest version from the Google Maven link. These APIs are in the alpha stage yet, so API is not final yet. These can be changed at any time. The article works with alpha04 version but may or may not work with earlier or later versions.


The Process for Activity Results API

The process to use this new API looks like this.

Activity Results API process

1. Create Contract

First, you have to either define your contract or use any existing one. A contract is the implementation of ActivityResultContract interface. Taken from the documentation, this is a contract specifying that an activity can be called with an input of type I and produce an output of type O.

Here’s a an example of SimpleContract which takes an Integer as input and returns some String data from the child Activity.

The createIntent() method is used to create the valid Intent which will be passed in startActivityForResult() method once this contract is called. And the parseResult() method will behave as a proxy for onActivityResult() method and is parsing the result Intent and extracting the data from it.

You can see that we don’t need to put bunch of if-else blocks in onActivityResult() method anymore. Because each case will be handled like this in separate implementation and separate parseResult() method. This is the beauty and simplicity of the Activity Results API.

2. Register Contract

The next step is to register this SimpleContract with the Activity Results API. This is done through calling registerForActivityResult() method.

Please note that in previous versions alpha02 and alpha03 , this method was called as prepareCall() . But in alpha04 , this was renamed to registerForActivityResult() .

You can see how easy it to register your contract. And with the simplicity of beloved Kotlin, you will get the result in a nice lambda method. This will be called after the parseResult() method from the contract and will give you the result (in our case a nullable String). You can check for the NULL value to see if user cancelled the action or the result was RESULT_OK from the child activity.

3. Call Contract

Finally, all you have to do is make the call to this new registration variable which is simpleContractRegistration in the above snippet. Go ahead and call it just like any ordinary method. And all the input you defined in the contract will be passed as type-safe parameters in your method call.

And voila. No more onActivityResult() now. You can easily use this with your own custom Activity or Fragmentclasses and it will be very clean, organized, and reusable code with type-safe parameters.


Pre-Built Contracts

Now that you have seen how simple it is to use the Activity Result API and create your own contracts. You will be super glad to know that Google has provided some very useful pre-built contracts. These can be accessed statically from the ActivityResultContracts class. Let’s explore some examples below.

Capturing Images — ActivityResultContracts.TakePicture()

You can easily capture the images from Camera without any hassle of Media Intent.


Pick Images — ActivityResultContracts.GetContent

This allows you to pick not only images but you can also select other files from your device. You have to provide mimeType of the files you need in app.


Requesting Permissions — ActivityResultContracts.RequestPermission

Now this is my most favorite one. I remember that I had to write like 100 lines of code to ask for multiple permissions to handle a very complex flow. I had written about it details in this article when there were no Activity Results API yet.

Multiple Runtime Permissions in Android Without Any Third-Party Libraries

The Activity Results API provides two methods — RequestPermission and RequestMultiplePermissions . These two does exactly what their names are. Here’s a quick example code.


Demo Code

I have created a simple demo project with some example contracts as discussed in this article on the following Github repository. You can explore more and play with it.

wajahatkarim3/ActivityResultsDemo


Conclusion

Wrapping it up now, in this article we discussed about some problems in the traditional onActivityResult() way. And then we got to learn about new Activity Results API. Then we created an example contract to see how different parts work together. And finally we saw some pre-built contracts like image capturing, picking images, permission handling etc.

At the end, please don’t forget to Subscribe to my newsletter to get more tutorials and tips on Android development directly in your inbox.


Thanks to Roberto Orgiu for reviewing and providing feedback on this post 🙌


If you liked this article, you can read my new articles below:


Wajahat Karim is Pakistan’s first Google Developer Expert in Android. As an experience Android developer, he deeply cares about it and keeps writing and speaking about it. He has written two worldwide 300+ pages books on Android development with more than 100 articles around the internet either on his website or his Medium publications. He is also a passionate contributor in open source and has created many Android libraries used by thousands of developers in their apps worldwide. As active public speaker, he spends lots of time giving talks in conferences and motivating people on Android development. You can find Wajahat most active on Twitter @WajahatKarim where he regularly shares all the good stuff about Android and community building.

Author's picture

Wajahat Karim

🔥 Google Dev Expert (GDE) in Android .
📱 Android Dev. 💻 Open Source Contributor . 📝 Technical Writer . 🎤 Public Speaker

Senior Android Developer

Karachi, Pakistan