1. OnActivityResult deprecation phenomenon and solution
After the update a few days ago AndroidX
, the project onActivityResult
was declared as @Deprecated
, roughly as shown in the picture below:
Since I am a code cleanliness, it will definitely not be very comfortable to look at this. I have no time, so I didn’t analyze it carefully, so I just Set it aside. I have a little time today, let's take a look at onActivityResult
how it was abandoned, and how to solve it now?
In fact, when we click on Activity#onActivityResult
the source code, we can see the solution explained by itself:
In Android
the code, it has been suggested that registerForActivityResult
the method instead, so what does this method mean? The answer is ComponentActivity
defined in:
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
At this time, I saw that there are many parameters, some of which are I
some O
, but in fact, it is the same after reading it several times. For each parameter we can talk a little more:
ActivityResultContract<I, O> contract
As in the title, we can literally translate it, we call it directly Activity结果契约类
, the official definition is:
A contract specifying that an activity can be called with an input of type I and produce an output of type O Makes calling an activity for result type-safe. Popular translation defines the input type I
and output type O of calling Activity
For the second parameter:
The direct translation of ActivityResultCallback<O>
is the callback of the Activity result
Now that we know the parameters, let's register according to the introduction:
private val activityResultContract = object : ActivityResultContract<Intent,ActivityResult>() {
override fun createIntent(context: Context, input: Intent): Intent = input
override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult = ActivityResult(resultCode,intent)
}
private val mActivityResultCallback = ActivityResultCallback<ActivityResult> {
Log.d("TAG", "show the resultCode: $ActivityResult")
}
We generally Intent
start Activity
it, so 输入I
it should be at this time Intent
;
if it is output, we generally need resultCode
and data
, yes, onActivityResult
the two parameters in:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
At this point, the system source code just provided us with a class that encapsulates resultCode
and Intent
, that is androidx.activity.result.ActivityResult
:
@SuppressLint("BanParcelableUsage")
public final class ActivityResult implements Parcelable {
private final int mResultCode;
@Nullable
private final Intent mData;
/**
* Create a new instance
*
* @param resultCode status to indicate the success of the operation
* @param data an intent that carries the result data
*/
public ActivityResult(int resultCode, @Nullable Intent data) {
mResultCode = resultCode;
mData = data;
}
Since we defined activityResultContract
and at this point mActivityResultCallback
, let's register it now:
class BaseActivity : AppCompatActivity() {
private val activityResultContract = object : ActivityResultContract<Intent,ActivityResult>() {
override fun createIntent(context: Context, input: Intent): Intent = input
override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult = ActivityResult(resultCode,intent)
}
private val mActivityResultCallback = ActivityResultCallback<ActivityResult> {
result -> Log.d("TAG","show the result : $result") }
private val launcher : ActivityResultLauncher<Intent> = registerForActivityResult(activityResultContract, mActivityResultCallback)
}
We have registered registerForActivityResult
, and an object is returned at this time ActivityResultLauncher
. In fact, you can tell by its name that this is the startup Activity
class:
/**
* A launcher for a previously-{@link ActivityResultCaller#registerForActivityResult prepared call}
* to start the process of executing an {@link ActivityResultContract}.
*
* @param <I> type of the input required to launch
*/
public abstract class ActivityResultLauncher<I> {
public void launch(@SuppressLint("UnknownNullness") I input)
}
At this point, we can use launcher
to start Activity
:
launcher.launch(Intent(this, MainActivity::class.java))
At this point, we have successfully started, and then mActivityResultCallback#onActivityResult
we can get the results we want in .
2. Use ActivityResultLauncher instead of onActivityResult principle
Generally, we use to Activity.startForResult
start an Intent, and then we Activity#onActivityResult
get a callback in the method, similar to this:
after using it ActivityResultLauncher
, the logic diagram is roughly as follows:
The steps are:
- Register the ActivityResultContract and ActivityResultCallback that need to be started with the ActivityResultRegistry, and generate the ActivityResultLauncher;
- The calling method of Activity.startForResult was originally needed, but now the ActivityResultLauncher agent is started;
- After starting ActivityB, ActivityA will still get the callback of onActivityForResult;
- At this point ActivityA will distribute callback data to ActivityResultRegistry;
- ActivityResultRegistry will find the target ActivityResultCallback according to the requestCode, thereby triggering the callback to achieve the effect of calling.
Basically, the general logic is almost clear. If you don’t understand anything, you can directly look at the source code. This source code should be relatively simple.
3. My experience in using it
If you usually use it ActivityResultLauncher
as a demo, it can still be used, but in real projects, it still needs to be packaged by yourself. The reasons are as follows:
- If you need to start multiple different activities in the Activity, then you need to write multiple ActivityResultCallbacks, because according to the logic of ActivityResultLauncher, a Launcher corresponds to an ActivityResultCallback.
- The readability of the code is not enough, which is mainly the reason for the first article, Launcher and ActivityResultCallback are separated.
In order to solve the above problems, of course, it is also for the convenience of calling. Generally, it is packaged by itself. There are many wheels. I found one on github:
https://github.com/TxcA/ManageStartActivity
Basically, after encapsulation, you can write:
startForResult(MainActivity:class, {
putExtra("userName","Tom")
putExtra("location","Shanghai")
}) {
code : Int, data : Intent? ->
// show the result
Log.d("TAG","resultCode : $code, data : $data")
}
This looks more elegant and very user-friendly, and I think this is the most human-friendly code. There is input, there are parameters, and then the result is output directly, no need to use startActivityForResult
, and then it is onActivityResult
obtained in .
Significance of onActivityResult deprecation
In fact , it registerForActivityResult
is based on Jetpack component
publishing. Of course, everything it does is to enable us ordinary developers to develop high-quality applications. onActivityResult
It's not unusable now, but with Android
the blessing Kotlin
of the package, registerForActivityResult
it is the icing on the cake to package some high-quality, easy-to-read and understandable code.