- Deep Dive into Activity Results API — No More onActivityResult()
- ActivityResultContracts by Examples
- Traditional Way — The onActivityResult() Method
- Introducing Activity Results API — The New Way
- Adding Dependencies
- The Process for Activity Results API
- 1. Create Contract
- 2. Register Contract
- 3. Call Contract
- Pre-Built Contracts
- Capturing Images — ActivityResultContracts.TakePicture()
- Pick Images — ActivityResultContracts.GetContent
- Requesting Permissions — ActivityResultContracts.RequestPermission
- Demo Code
- Conclusion
- Realtime Database vs Firestore: 9 Major Differences
- April 17, 2020
- What Happened to Chat Bubbles / Heads in Android?
- April 10, 2020
- Activity Result Contract – The Basics
- Background
- Contracts
- Benefits
- Conclusion
- Android Activity Result Contracts
- Before Activity Result Contracts
- Built-In Contracts
- Take a Picture / Video
- Request Permission(s)
- And More!
- Custom Contracts
- Getting Started
- Example Contract
- “Gotchas”
- Takeaways
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.
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.
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 Fragment classes 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.
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.
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:
Realtime Database vs Firestore: 9 Major Differences
Firestore is much better than the Realtime Database like the black trainer. Period. When it comes to cloud and database hosting, there are several options available for the developers such as Firebase, Firestore, AWS, Google Cloud and so on.
April 17, 2020
What Happened to Chat Bubbles / Heads in Android?
How the SYSTEM_ALERT_WINDOW permission has been abused over the years by malware creators? Chat Bubbles aka Chat Heads is a user interface element initially introduced by Facebook Messenger back in December 2012.
April 10, 2020
7 years experience. 💻 Creator of various Open Source libraries on Android . 📝 Author of two technical books and 100+ articles on Android. 🎤 A passionate Public Speaker giving talks all over the world.
Источник
Activity Result Contract – The Basics
As regular readers of Styling Android will know, I generally publish sample code projects along with each series of articles. Whenever the code requires runtime permissions my heart sinks because I know that I must add a chunk of boilerplate. This not only means extra work for me, but it can also make the sample code more difficult to follow. However, there is a relatively new addition to AndroidX Activity which simplifies this somewhat: ActivityResultContracts .
Background
Requesting runtime permissions has been a requirement since API 23 (Marshmallow) which was released in 2015. To request permission from the user, we need to pass control to the OS to perform this request. This generally uses the startActivityForResult() flow (if not directly, then indirectly) so that we’ll get a specific method invoked when control is returned to us.
A new pattern that was introduced in the Jetpack Activity library 1.2.0-alpha02 which replaces startActivityForResult() followed by an invocation of onActivityResult() . We can use this new pattern wherever we previously used startActivityForResult() – it is not restricted to runtime permissions. For example, we can use it to take a picture, or open a document, etc.
Contracts
The new APIs are based on different contracts which are specific to each use-case. For example, when requesting runtime permission, we need to know whether the permission has been granted. However, when we request the taking of a picture, the result needs to be a Uri to the picture that was taken. An example will help to explain this:
Here the contract is ActivityResultsContracts.RequestPermission() . It extends ActivityResultsContract which is a generic class with two type parameters representing the input and output types. For a permission request, the input is a string representing the permission, and the output is a boolean indicating whether the permission has been granted.
Internally this contract implements the logic for requesting the required permission.
The registerForActivityResult() function takes two arguments. The first is the contract, and the second is a lambda which will be invoked on completion. The output type of the contract dictates the argument type of the lambda. In this example, it is a boolean that indicates whether the permission was granted.
The registerForActivityResult() function returns an ActivityResultLauncher which we can invoke when we want to perform the operation:
Here we first check whether we already have the required permission. If so, we navigate to the appropriate destination. If we don’t have the necessary permission we check whether we should show thew request permission rationale. Otherwise, we launch the ActivityResultLauncher instance that we created earlier. The input type of the contract dictates the argument for the launch method. In this case it is a string representing the required permission.
There is a separate contract that we can use for requesting multiple permissions in a single operation.
Benefits
This approach makes the code much easier to understand, imo. Therefore it is easier to maintain because someone viewing it for the first time requires less cognitive load to understand the logic.
The previous pattern of startActivityForResult() / onActivityResult() required un understanding of how this mechanism works. However, looking at requestPermissions.launch(. ) should lead directly to the lambda which will be invoked on completion.
Moreover, this does not require us to even know that control is passing elsewhere while this is running. It is an opaque box operation and we’re agnostic of the details. All that we need to know is that we launch the contract and the lambda is invoked with the result once it completes.
There are a number of ready-made contracts – just look at the known subclasses of ActivityResultContract for a list. Further to that, it is actually fairly trivial to create our own subclass of ActivityResultContract to create custom logic.
Conclusion
ActivityResultContracts are a really useful addition. As I have already mentioned, I think that it greatly improves the understandability of our code. That said, this is one specific use-case, and in the next article, we’ll delve a little deeper.
The source code for this article is available here.
Источник
Android Activity Result Contracts
Recently Google introduced Android ActivityResultContracts which can be used to pass data between activities. These contracts are a huge improvement compared to onActivityResult() as they allow for the decoupling of unrelated data transfers and can be easily reused across multiple activities. ActivityResultContracts remove the need for conditional statements that were all too common a pattern with the legacy of onActivityResult() when deciding “what data is this / where is it from.»
Before Activity Result Contracts
Previously, passing data from an exiting activity to the launching activity or handling permission request results would require creating Intents and overriding onActivityResult() and using some if / else or switch statements (or when statements if you prefer Kotlin). While this is easy enough to use, if your activity may need to handle the result of a variety of exiting intents / permission results. The onActivityResult() method can quickly become a long and complex mess that isn’t the easiest to read.
Suppose our MainActivity needs to handle requesting microphone, camera, and location permissions in addition to a string message received from another activity. That might look something like this.
Built-In Contracts
Google has already provided some commonly used ActivityResultContracts that can be directly passed into registerForActivityResult() along with a callback that will be executed when that activity result is found.
Take a Picture / Video
Easily capture images / videos from the Camera without having to deal with Media Intent. For photos use TakePicture() and for videos use TakeVideo() .
Request Permission(s)
Ask the user to grant Runtime Permission(s). To request one permission use RequestPermission() which will return a single Boolean indicating if the permission was granted.
To request multiple permissions simultaneously use RequestMultiplePermissions() which would return a map of Booleans indicating which permissions were granted.
And More!
Custom Contracts
Developers also have the ability to define and use their own custom Activity Result Contracts to handle Intents passed between activities. This allows developers to specify expected input and output types and easily reuse across multiple activities.
Getting Started
Activity Result APIs were introduced in AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02 and they provide components for registering for a result, launching the result, and handling the result once it is dispatched by the system.
Add the following dependencies to the app’s build.gradle file. (At time of writing these were the latest versions of activity-ktx and fragment-ktx . Feel free to use any version later than this.)
Example Contract
Note: We will be using View Binding for code simplicity.
For this example, we want a simple MessageContract that will return a String from one activity to another.
Let’s start by defining a MessageContract which will create an Intent launching MessageActivity .
And a simple MessageActivity layout could contain an EditText and Button defining an onClick method called onSubmitMessage() . Here’s the MessageActivity layout XML from our code.
Let’s define how MessageActivity should handle the button click in onSubmitMessage() — by packaging the String inputted by the user in the EditText in an Intent, setting the Activity result to RESULT_OK , and finishing the activity with finish() .
When the MessageActivity is closed, the resulting String can be parsed from the Intent and returned in the MessageContract’s parseResult() .
Note that in the above example we pass in Unit as the input and a String as the output. If you wanted to provide arguments to the next activity, you would do so in the input portion. We just use Unit when you want to specify the input as nothing.
In our receiving activity, we need to specify a variable for the ActivityResultLauncher returned by the registerForActivityResult() where we pass in our custom MessageContract() .
Here we will define a lambda that is to be executed when the contract parses the result. In this example, we are just displaying the String message in a Toast to the user. The result it is expected to be a String as was specified to be the output type by our contract — class MessageContract : ActivityResultContract () .
To launch the messageLauncher in our receiving activity, we simply need to call messageLauncher.launch() . This will call the createIntent() method we overrode in our custom MessageContract and create and launch the MessageActivity Intent.
“Gotchas”
Be sure your contracts are available when the Activities process is created or recreated. It is possible that your process gets destroyed while the other Activity that you are awaiting your result from is working. If this happens and your result contract isn’t prepared by the time your Activity is recreated than the result will be missed. Do NOT make these local registrations that happen when a specific function is hit or button is pressed, which is also called out in Google’s documentation here.
Takeaways
ActivityResultContracts can be used to explicitly specify input and output types when handling the different data transfer cases between activities. You no longer need to override onActivityResult() and use a plethora of conditional statements to determine how to handle the exiting activity’s Intent. The contracts allow for the (1) decoupling of unrelated data transfers (2) easy reuse across multiple activities and (3) increased type-safety. They are definitely a clean, long-needed improvement for Android development.
Feel free to check out the sample application provided with this post.
Источник