Activity#onActivityResult is deprecated, what should I do?

1. OnActivityResult deprecation phenomenon and solution


After the update a few days ago AndroidX, the project onActivityResultwas declared as @Deprecated, roughly as shown in the picture below:
insert image description here
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 onActivityResulthow it was abandoned, and how to solve it now?

In fact, when we click on Activity#onActivityResultthe source code, we can see the solution explained by itself:
insert image description here

In Androidthe code, it has been suggested that registerForActivityResultthe method instead, so what does this method mean? The answer is ComponentActivitydefined 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 Isome 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 Intentstart Activityit, so 输入Iit should be at this time Intent;
if it is output, we generally need resultCodeand data, yes, onActivityResultthe 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 resultCodeand 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 activityResultContractand 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 Activityclass:


/**
 * 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 launcherto start Activity:

launcher.launch(Intent(this, MainActivity::class.java))

At this point, we have successfully started, and then mActivityResultCallback#onActivityResultwe can get the results we want in .

2. Use ActivityResultLauncher instead of onActivityResult principle


Generally, we use to Activity.startForResultstart an Intent, and then we Activity#onActivityResultget a callback in the method, similar to this:
insert image description here
after using it ActivityResultLauncher, the logic diagram is roughly as follows:

insert image description here
The steps are:

  1. Register the ActivityResultContract and ActivityResultCallback that need to be started with the ActivityResultRegistry, and generate the ActivityResultLauncher;
  2. The calling method of Activity.startForResult was originally needed, but now the ActivityResultLauncher agent is started;
  3. After starting ActivityB, ActivityA will still get the callback of onActivityForResult;
  4. At this point ActivityA will distribute callback data to ActivityResultRegistry;
  5. 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 ActivityResultLauncheras a demo, it can still be used, but in real projects, it still needs to be packaged by yourself. The reasons are as follows:

  1. 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.
  2. 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 onActivityResultobtained in .

Significance of onActivityResult deprecation

In fact , it registerForActivityResultis based on Jetpack componentpublishing. Of course, everything it does is to enable us ordinary developers to develop high-quality applications. onActivityResultIt's not unusable now, but with Androidthe blessing Kotlinof the package, registerForActivityResultit is the icing on the cake to package some high-quality, easy-to-read and understandable code.

Guess you like

Origin blog.csdn.net/u013762572/article/details/124631857