When you start another activity, whether it’s an activity in your app or from another application, it doesn’t need to be a one-way activity. You can also start an acitivty and get the result again. For example, your application can launch the camera application and receive the captured image as a result. Or, you can start the Contacts app so the user selects a contact and as a result you will get contact details.
Although the basic startActivityForResult () and onActivityResult () APIs are available in the Activity class on all API levels, I recommend using the Activity Result APIs introduced in Activity AndroidX 1.2.0-alpha02 and Fragment. 1.3.0-alpha02.
The Activity Result APIs provide components to register results, launch the results, and process the results after the system sends them.
To be more specific about how to use the Activity Result APIs , I will introduce the following two cases using the Activity Result APIs . (Note there are many other similar cases for using API result)
1. Request Runtime Permissions
Recently, Android stopped using the old ActivityResult API and introduced new APIs with AndroidX Activity 1.2.0-alpha02. This makes request runtime permissions easier. With the new APIs available, developers can register results, launch for results, and then process the results generated by the system.
But Why?
When we start activity for a result, there is a possibility that your process and activity will be destroyed due to lack of memory, for activities that require a lot of memory like using the camera. Hence, the result APIs work separating the result callback from the location in your code where the other activity is launched.
Hence, unfortunately when the process or activity is re-created, the callback has to register it all the time, even if another start Activity logic just happens based on user input or other business logic.
What is different?
Previously, we had to manage request code while requesting permission by passing it to requestPermissions () method . And then use that request code while fetching result in the OnRequestPermissionResult () method as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token class-name">ActivityCompat</span> <span class="token punctuation">.</span> <span class="token function">requestPermissions</span> <span class="token punctuation">(</span> context <span class="token punctuation">,</span> permissionArray <span class="token punctuation">,</span> requestCode <span class="token punctuation">)</span> override fun <span class="token function">onRequestPermissionsResult</span> <span class="token punctuation">(</span> requestCode <span class="token operator">:</span> <span class="token class-name">Int</span> <span class="token punctuation">,</span> permissions <span class="token operator">:</span> <span class="token class-name">Array</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">String</span> <span class="token punctuation">></span></span> <span class="token punctuation">,</span> grantResults <span class="token operator">:</span> <span class="token class-name">IntArray</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> when <span class="token punctuation">(</span> requestCode <span class="token punctuation">)</span> <span class="token punctuation">{</span> REQUEST_CODE <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> grantResults <span class="token punctuation">.</span> <span class="token function">isNotEmpty</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">&&</span> grantResults <span class="token punctuation">[</span> <span class="token number">0</span> <span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token class-name">PackageManager</span> <span class="token punctuation">.</span> PERMISSION_GRANTED <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Permission is granted.</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">//permission is denied.</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
With the new Activity Result API, we won’t have to manually manage the request codes because the system handles it internally.
The new Activity Result API provides the registerForActivityResult () API to register the result callback command. It needs an ActivityResultContract and an ActivityResultCallback and returns an ActivityResultLauncher , using them we can launch another activity as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">class</span> <span class="token class-name">MainActivity</span> <span class="token operator">:</span> <span class="token class-name">AppCompatActivity</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> override fun <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> <span class="token class-name">Bundle</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> <span class="token function">setContentView</span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> layout <span class="token punctuation">.</span> activity_main <span class="token punctuation">)</span> findViewById <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Button</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> id <span class="token punctuation">.</span> button <span class="token punctuation">)</span> <span class="token punctuation">.</span> setOnClickListener <span class="token punctuation">{</span> activityResultLauncher <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> <span class="token class-name">Manifest</span> <span class="token punctuation">.</span> permission <span class="token punctuation">.</span> CAMERA <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> val activityResultLauncher <span class="token operator">=</span> <span class="token function">registerForActivityResult</span> <span class="token punctuation">(</span> <span class="token class-name">ActivityResultContracts</span> <span class="token punctuation">.</span> <span class="token class-name">RequestPermission</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> isGranted <span class="token operator">-></span> <span class="token comment">// Handle Permission granted/rejected</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> isGranted <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Permission is granted</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Permission is denied</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- ActivityResultContract: specifies the type of input needed to generate result. In the example above ActivityResultContracts.RequestPermission () is used which means the acticity launcher will require android permissions as input input.
- ActivityResultCallback: specifies how we will treat the user’s response to the authorization request.
- When the launch () method is called with CAMERA permission as input, the permission dialog (appears in request permison dialog) asking for CAMERA permission will be displayed. Based on the user’s response to the permission, the system calls activityResultCallback .
Similarly, many other permissions can be requested at the same time by passing a permissions array instead of a single permissions as input and then we get a MutableMap with the permission key and grant result as value in activityResultCallback .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <span class="token keyword">class</span> <span class="token class-name">MainActivity</span> <span class="token operator">:</span> <span class="token class-name">AppCompatActivity</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> override fun <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> <span class="token class-name">Bundle</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> <span class="token function">setContentView</span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> layout <span class="token punctuation">.</span> activity_main <span class="token punctuation">)</span> findViewById <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Button</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> id <span class="token punctuation">.</span> button <span class="token punctuation">)</span> <span class="token punctuation">.</span> setOnClickListener <span class="token punctuation">{</span> activityResultLauncher <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> <span class="token function">arrayOf</span> <span class="token punctuation">(</span> <span class="token class-name">Manifest</span> <span class="token punctuation">.</span> permission <span class="token punctuation">.</span> CAMERA <span class="token punctuation">,</span> <span class="token class-name">Manifest</span> <span class="token punctuation">.</span> permission <span class="token punctuation">.</span> READ_EXTERNAL_STORAGE <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> val activityResultLauncher <span class="token operator">=</span> <span class="token function">registerForActivityResult</span> <span class="token punctuation">(</span> <span class="token class-name">ActivityResultContracts</span> <span class="token punctuation">.</span> <span class="token class-name">RequestMultiplePermissions</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> permissions <span class="token operator">-></span> <span class="token comment">// Handle Permission granted/rejected</span> permissions <span class="token punctuation">.</span> entries <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> val permissionName <span class="token operator">=</span> it <span class="token punctuation">.</span> key val isGranted <span class="token operator">=</span> it <span class="token punctuation">.</span> value <span class="token keyword">if</span> <span class="token punctuation">(</span> isGranted <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Permission is granted</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Permission is denied</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
2. Launching an activity for get content result
While registerForActivityResult () registers your callback, it doesn’t start another activity and starts asking for result. Instead, it is the responsibility of the ActivityResultLauncher instance that is returned.
If the input for the launch () method exists, the launcher will take input matching the ActivityResultContract type. Calling the launch () method will initiate the result generation process. When the user completes the next activity and returns the activity, onActivityResult () from ActivityResultCallback will then be executed, as shown in the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token class-name">ActivityResultLauncher</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">String</span> <span class="token punctuation">></span></span> mGetContent <span class="token operator">=</span> <span class="token function">registerForActivityResult</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">GetContent</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">ActivityResultCallback</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Uri</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onActivityResult</span> <span class="token punctuation">(</span> <span class="token class-name">Uri</span> uri <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Handle the returned Uri</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@Nullable</span> savedInstanceState <span class="token operator">:</span> <span class="token class-name">Bundle</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token class-name">Button</span> selectButton <span class="token operator">=</span> <span class="token function">findViewById</span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> id <span class="token punctuation">.</span> select_button <span class="token punctuation">)</span> <span class="token punctuation">;</span> selectButton <span class="token punctuation">.</span> <span class="token function">setOnClickListener</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">OnClickListener</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onClick</span> <span class="token punctuation">(</span> <span class="token class-name">View</span> view <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Pass in the mime type you'd like to allow the user to select</span> <span class="token comment">// as the input</span> mGetContent <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> <span class="token string">"image/*"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Above, we call launch (“image /”) , the parameter is a string mime type image, the launcher will take input matching ActivityResultContract here is GetContent () and will return the results. Conforming to the GetContent ResultContact is the Uri itself. There are also many other similar input types.
Receiving an activity result in a separate class
Get the result activity in a separate class.
While the ComponentActivity and Fragment classes implement the ActivityResultCaller interface to allow you to use the registerForActivityResult () APIs, you can also get the result activity in a separate class without implementing ActivityResultCaller directly using ActivityResultRegistry.
For example, you might want to deploy LifecycleObserver which handles contract signing along with launching the launcher:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <span class="token keyword">class</span> <span class="token class-name">MyLifecycleObserver</span> <span class="token keyword">implements</span> <span class="token class-name">DefaultLifecycleObserver</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ActivityResultRegistry</span> mRegistry <span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">ActivityResultLauncher</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">String</span> <span class="token punctuation">></span></span> mGetContent <span class="token punctuation">;</span> <span class="token class-name">MyLifecycleObserver</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">ActivityResultRegistry</span> registry <span class="token punctuation">)</span> <span class="token punctuation">{</span> mRegistry <span class="token operator">=</span> registry <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LifecycleOwner</span> owner <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> mGetContent <span class="token operator">=</span> mRegistry <span class="token punctuation">.</span> <span class="token function">register</span> <span class="token punctuation">(</span> “key” <span class="token punctuation">,</span> owner <span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">GetContent</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">ActivityResultCallback</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Uri</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onActivityResult</span> <span class="token punctuation">(</span> <span class="token class-name">Uri</span> uri <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Handle the returned Uri</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">selectImage</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Open the activity to select an image</span> mGetContent <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> <span class="token string">"image/*"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">MyFragment</span> <span class="token keyword">extends</span> <span class="token class-name">Fragment</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">MyLifecycleObserver</span> mObserver <span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">void</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> <span class="token class-name">Bundle</span> savedInstanceState <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> mObserver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyLifecycleObserver</span> <span class="token punctuation">(</span> <span class="token function">requireActivity</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getActivityResultRegistry</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">getLifecycle</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">addObserver</span> <span class="token punctuation">(</span> mObserver <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">void</span> <span class="token function">onViewCreated</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">View</span> view <span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Bundle</span> savedInstanceState <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Button</span> selectButton <span class="token operator">=</span> <span class="token function">findViewById</span> <span class="token punctuation">(</span> <span class="token class-name">R</span> <span class="token punctuation">.</span> id <span class="token punctuation">.</span> select_button <span class="token punctuation">)</span> <span class="token punctuation">;</span> selectButton <span class="token punctuation">.</span> <span class="token function">setOnClickListener</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">OnClickListener</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onClick</span> <span class="token punctuation">(</span> <span class="token class-name">View</span> view <span class="token punctuation">)</span> <span class="token punctuation">{</span> mObserver <span class="token punctuation">.</span> <span class="token function">selectImage</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
When using ActivityResultRegistry APIs, you should use APIs that use LifecycleOwner, since LifecycleOwner will automatically delete your registered launcher when lifecycle onDestroy is destroyed. However, in the event that LifecycleOwner is not available, each ActivityResultLauncher class still allows you to manually call unregister () instead.
Creating a custom contract
Updating….
For more details, you can see the official documentation here:
- https://developer.android.com/training/basics/intents/result
- https://developer.android.com/training/permissions/requesting
Reference source: https://medium.com/codex/android-runtime-permissions-using-registerforactivityresult-68c4eb3c0b61