Activity#onActivityResult被弃用了,该怎么办?

Posted microhex

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity#onActivityResult被弃用了,该怎么办?相关的知识,希望对你有一定的参考价值。

1. onActivityResult弃用现象和解决方法


前几天更新了androidX之后,项目中的onActivityResult就被声明为@Deprecated,大概如下图:

由于本人呢,是一个代码洁癖者,看着这个肯定不会很舒服,苦于没有时间,就没有仔细去好好分析,就放在一边呢。今天有点时间,我们就来扒一扒,看看onActivityResult是如何被弃用了,现在又该如何解决呢?

其实,我们点击Activity#onActivityResult的源码的时候,就可以看到它自己说明的解决方案:

Android代码中,已经提示了需要使用 registerForActivityResult方法来替代,那么这个方法是什么意思呢?答案在ComponentActivity定义了:

    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) 
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    

此时看着参数比较多,有I又有O的,其实多看几遍也就那样。对于每一个参数我们可以多聊几下:

ActivityResultContract<I, O> contract

如题我们可以从字面上去翻译他,我们直接叫Activity结果契约类, 官方的定义为:

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.
通俗的翻译为 定义了 调用Activity的输入类型I 和 输出类型O

对第二个参数:

ActivityResultCallback<O>
直接翻译就是Activity结果的回调

既然知道了参数,那么我们就来按照介绍,来注册一下:

    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")
 

我们一般都是Intent启动Activity的,所以此时输入I应该为Intent;
如果输出呢,我们一般需要resultCodedata,对,就是onActivityResult中的两个参数:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) 

此时,正好系统源码给我们提供了一个类,封装了resultCodeIntent,那就是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;
    

    

由于此时我们定义了activityResultContractmActivityResultCallback,我们现在来注册一下:

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)


我们注册了registerForActivityResult, 此时返回一个ActivityResultLauncher的对象,其实看它的名字就知道,这个就是启动Activity的类:


/**
 * 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)

此时,我们就可以使用launcher去启动Activity了:

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

此时,我们就启动成功了,然后在mActivityResultCallback#onActivityResult中获取我们想要的结果了。

2. 使用 ActivityResultLauncher 替代 onActivityResult 原理


一般,我们使用Activity.startForResult启动一个Intent,然后我们在Activity#onActivityResult方法中得到回调,类似于这种:

在使用了 ActivityResultLauncher之后,逻辑图就大概如下了:


其步骤为:

  1. 向 ActivityResultRegistry 注册需要启动的 ActivityResultContract 和 ActivityResultCallback,并生成了 ActivityResultLauncher;
  2. 本来需要Activity.startForResult的调用方法,现在有ActivityResultLauncher代理启动;
  3. 启动ActivityB完成之后,ActivityA还是会获取到onActivityForResult的回调;
  4. 此时ActivityA会分发回调数据给 ActivityResultRegistry;
  5. ActivityResultRegistry会根据 requestCode来来找到目标ActivityResultCallback,从而触发回调,达到调用的效果。

基本上大致的逻辑就差不多将清楚了,有什么不懂的可以直接看看源码,这个源码应该算是比较简单的。

3. 自己使用的心得

如果平时你使用ActivityResultLauncher作为Demo的话,还是可以使用的,但是真正的项目上,还是需要自己封装的,原因如下:

  1. 如果你需要在Activity中启动多个不同的Activity,那么你就需要写多个 ActivityResultCallback了,因为按照 ActivityResultLauncher的逻辑,一个Launcher是对应一个ActivityResultCallback的。
  2. 代码的可读性不够,也主要是第一条的原因,Launcher和ActivityResultCallback是分开的。

为了解决以上的问题,当然也是为了自己调用方便,一般都是自己去封装的,轮子比较多,我在github找到的一个:

https://github.com/TxcA/ManageStartActivity

基本上封装之后可以这么写:

 startForResult(MainActivity:class, 
         putExtra("userName","Tom")
         putExtra("location","Shanghai")
     
      )  code : Int, data : Intent? -> 
        // show the result 
        Log.d("TAG","resultCode : $code, data : $data")
    

这样看上去就比较优雅了,而且非常的人性化,而且我觉得这个是最符合人思路的代码了。有输入,有参数,然后就直接输出结果了,不需要使用startActivityForResult,然后又是在onActivityResult中获取了。

onActivityResult 弃用的意义

其实registerForActivityResult是基于Jetpack component发布的,它所做的一切当然还是为了让我们普通开发者能够开发出高质量的应用。onActivityResult现在也不是不能用,但是Android拥有了Kotlin的加持,借助registerForActivityResult封装出一些高质量,易阅读和理解的代码,都是锦上添花的意义了。

以上是关于Activity#onActivityResult被弃用了,该怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Activity 类以外的 onActivityResult 方法

onactivityresult啥时候执行

从另一个 Activity 返回的错误 requestCode onActivityResult

如何从小部件收听已关闭的活动? (onActivityResult只在Activity中)

如果 Activity 进入 PictureInPicture 模式,则不会调用 onActivityResult

尝试使用 Kotlin 和 onActivityResult 将数据从 Fragment 发送到 Activity