startActivityForResult被标记为废弃?Activity Result API闪亮登场!

2,829 阅读9分钟

本文已同步发表于我的微信公众号,搜索 代码说 即可关注,欢迎与我沟通交流。

startActivityForResult()被标记为过时

一般我们声明的Activity都会继承自 AppCompatActivity (引入androidx.appcompat:appcompat:xxx 库),并且AppCompatActivity -> FragmentActivity -> ComponentActivity (->表示继承)

当我们需要跳转到另一个Activity并需要拿到返回结果时,可以使用startActivityForResult()来实现。然而随着相应库版本的提高,突然有一天,你会发现startActivityForResult()/onActivityResult()被标记成过时方法了,如下: startActivityForResult废弃 点进方法内部:

//ComponentActivity.java
    /**
     * {@inheritDoc}
     *
     * @deprecated use
     * {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
     * passing in a {@link StartActivityForResult} object for the {@link ActivityResultContract}.
     */
    @Override
    @Deprecated //被标记为过时
    public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
            int requestCode) {
        super.startActivityForResult(intent, requestCode);
    }

   /**
     * {@inheritDoc}
     *
     * @deprecated use
     * {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
     * with the appropriate {@link ActivityResultContract} and handling the result in the
     * {@link ActivityResultCallback#onActivityResult(Object) callback}.
     */
    @CallSuper
    @Override
    @Deprecated //被标记为过时
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

ComponentActivity是通过androidx.activity:activity这个库引入的,通过查看该库的版本更新记录发现是在 androidx.activity:activity:1.2.0-alpha04 中将其废弃的,所以在这个版本之后,当使用startActivityForResult()时,都会看到过时提醒并推荐使用registerForActivityResult()替代。 1.2.0-alpha04

registerForActivityResult替代方案

使用示例

//第一个Activity
class ResultApiActivity : AppCompatActivity() {

  //1、注册回调函数
  private var resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        //处理返回的结果
        val code = result.resultCode //返回码 如:Activity.RESULT_OK、Activity.RESULT_CANCELED
        val data = result.data
        log("resultCode:$code,data:${intent?.getStringExtra(ResultApi2Activity.KEY_TRANSFER)}")
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_result_api)
    mTvResultApi.setOnClickListener {
        //2、启动Intent跳转
        resultLauncher.launch(Intent(this, ResultApi2Activity::class.java))
      }
  }
}

//第二个Activity
class ResultApi2Activity : AppCompatActivity() {
    companion object{
        const val KEY_TRANSFER = "key_transfer"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
    }
}
//执行结果: resultCode:-1, data:i'm value from ResultApi2Activity

首先1处在 ComponentActivityFragment 中调用registerForActivityResult()注册一个回调函数来处理其他页面返回的结果,registerForActivityResult() 接受 ActivityResultContractActivityResultCallback 作为参数:

  • ActivityResultContract<I,O>:定义生成结果所需的输入类型(I)输出类型(O),可为Activity传值拍照请求权限等基本 intent 操作提供默认协定,还可以创建自定义协定。
  • ActivityResultCallback< O>:本身是interface类,内部有一个名为onActivityResult(O result)的方法,参数类型OActivityResultContract<I,O>中定义的输出类型O对象。

registerForActivityResult()方法执行后,返回ActivityResultLauncher类型,用来启动另一个activity,但是此时还没有启动,需要调用ActivityResultLauncher#launch()进行启动

registerForActivityResult()不仅可以用在startActivityForResult()的场景下,还可以用在其他场景下,如权限请求:

private var permissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        for ((string, isGrant) in permissions) {
            log("$string 权限申请状态:$isGrant")
            /**
             * 如执行结果:
             * 1、android.permission.CAMERA 权限申请状态:true
             * 2、android.permission.WRITE_EXTERNAL_STORAGE 权限申请状态:true
             */
        }
    }

//权限申请
mTvPermission.setOnClickListener {
     permissionLauncher.launch(
         arrayOf( Manifest.permission.CAMERA,  Manifest.permission.WRITE_EXTERNAL_STORAGE)
)}

可以看到ActivityResultContract参数传入的是ActivityResultContracts.RequestMultiplePermissions(),所以传入不同的ActivityResultContract就可以用于不同的场景中,那么都可以用于什么场景呢?

ActivityResultContracts 场景

上一节示例中,registerForActivityResult()的第一个参数我们传入的是ActivityResultContracts.StartActivityForResult()、RequestMultiplePermissions(),其是ActivityResultContract接口的具体实现类,我们点进ActivityResultContracts看看其他实现类:

ActivityResultContracts.xxxlaunch()入参结果回调说明
StartActivityForResultIntentActivityResult(resultCode, intent)启动另一个Activity并接收其返回结果
RequestPermissionString,如:Manifest.permission.CAMERABoolean请求一个权限并接收其授权结果
RequestMultiplePermissionsString[],如arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE)Map<String, Boolean>,如:for ((permission, isGranted) in permissions) {if (isGranted) {// 权限已授权} else {// 权限未授权}}请求多个权限并接收其授权结果
CaptureVideoUriBoolean用于获取视频,并将其保存到提供的Uri中
@RequiresApi(19) CreateDocumentString,如:launcher.launch("document.txt")Uri创建一个新的文档并返回其URI
GetContentString,如:launcher.launch("image/*")Uri打开文件选择器并获取所选文件的URI
@RequiresApi(18) GetMultipleContentsStringList< Uri>打开文件选择器并获取所选多个文件的URI
@RequiresApi(19) OpenDocumentString[]Uri打开现有文档并返回其URI
@RequiresApi(21) OpenDocumentTreeUriUri打开文档树并返回所选目录的URI
@RequiresApi(19) OpenMultipleDocumentsString[]List< Uri>打开多个现有文档并返回它们的URI
PickContactVoidUri打开联系人应用程序并返回所选联系人的URI
StartIntentSenderForResultIntentSenderRequestActivityResult启动一个IntentSender并接收其返回结果
TakePictureUriBoolean启动相机应用程序并拍摄照片
TakePicturePreviewVoidBitmap启动相机应用程序并拍摄预览照片

上述都是系统已经给我们定义好的行为(API 31),通常来说已经够我们平时开发使用了,不过系统还是提供了自定义ActivityResultContract的能力。

自定义ActivityResultContract

自定义ActivityResultContract可以让我们根据自己的需求创建一个新的ActivityIntent操作,并使用registerForActivityResult()方法将其与ActivityResultLauncher对象绑定。以下是一个自定义ActivityResultContract的示例:

/**
 * 1、自定义ActivityResultContract
 */
class CustomContract : ActivityResultContract<Void, String>() {
    companion object {
        const val DEFAULT_VALUE = "default_value"
    }

    /**
     * 创建Intent
     * @param context Context
     * @param input 当前类的第一个泛型参数
     * @return
     */
    override fun createIntent(context: Context, input: Void?): Intent {
        return Intent(context, ResultApi2Activity::class.java)
    }

    /**
     * 解析结果,类似于Activity#onActivityResult
     * @param resultCode 返回码 [Activity.setResult] 的 resultCode
     * @param intent [Activity.setResult] 的 intent
     * @return
     */
    override fun parseResult(resultCode: Int, intent: Intent?): String {
        if (resultCode != Activity.RESULT_OK || intent == null) return DEFAULT_VALUE
        return intent.getStringExtra(ResultApi2Activity.KEY_TRANSFER) ?: DEFAULT_VALUE
    }

    /**
     * 直接获取同步结果,可以用于中间请求拦截判断使用,
     * 如ActivityResultContracts.RequestPermission中的该方法重写
     */
    override fun getSynchronousResult(context: Context, input: Void?): SynchronousResult<String>? {
        return super.getSynchronousResult(context, input)
    }
}

 //2、Activity中使用自定义Contract
 private val mLauncher =
        registerForActivityResult(CustomContract()) { result -> log("return result:$result") }
 //点击事件中触发launch
 mTvCustomContract.setOnClickListener { mLauncher.launch(null)}

//3、目标Activity
class ResultApi2Activity : AppCompatActivity() {
    companion object{
        const val KEY_TRANSFER = "key_transfer"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
    }
}

首先自定义ActivityResultContract,重写了其中的createIntent()parseResult()方法。

  • createIntent()方法用于创建一个新的Intent对象,因为输入参数为Void,所以不需要传数据给目标Activity
  • parseResult()方法用于解析目标Activity返回的结果,并将其转换为输出类型String

接着,使用registerForActivityResult()方法将CustomContractActivityResultLauncher对象绑定,并在需要启动该Activity时使用该对象的launch()启动它。

源码浅析

上述示例中主要涉及两个方法,分别为ComponentActivity#registerForActivityResult()ActivityResultLauncher#launch()方法,下面重点看下这两个方法。

registerForActivityResult()

    //ComponentActivity.java
    private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry(){...}
    
    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    }

    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultRegistry registry,
            @NonNull final ActivityResultCallback<O> callback) {
        return registry.register(
                "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
    }

可以看到最终调用了ActivityResultRegistry#register()方法:

    public final <I, O> ActivityResultLauncher<I> register(
            @NonNull final String key,
            @NonNull final LifecycleOwner lifecycleOwner,
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultCallback<O> callback) {

        Lifecycle lifecycle = lifecycleOwner.getLifecycle();

        //1、判断Lifecycle的状态必须小于Lifecycle.State.STARTED,否则直接抛异常
        if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
            throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
                    + "attempting to register while current state is "
                    + lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
                    + "they are STARTED.");
        }

        //2、生成唯一的 requestCode
        final int requestCode = registerKey(key);
        //3、获取或初始化生命周期容器LifecycleContainer
        LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
        if (lifecycleContainer == null) {
            lifecycleContainer = new LifecycleContainer(lifecycle);
        }
        //4、注册观察者并将其添加到LifecycleContainer中
        LifecycleEventObserver observer = new LifecycleEventObserver() {
            @Override
            public void onStateChanged(
                    @NonNull LifecycleOwner lifecycleOwner,
                    @NonNull Lifecycle.Event event) {
                if (Lifecycle.Event.ON_START.equals(event)) {
                    mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
                    if (mParsedPendingResults.containsKey(key)) {
                        final O parsedPendingResult = (O) mParsedPendingResults.get(key);
                        mParsedPendingResults.remove(key);
                        callback.onActivityResult(parsedPendingResult);
                    }
                    final ActivityResult pendingResult = mPendingResults.getParcelable(key);
                    if (pendingResult != null) {
                        mPendingResults.remove(key);
                        callback.onActivityResult(contract.parseResult(
                                pendingResult.getResultCode(),
                                pendingResult.getData()));
                    }
                } else if (Lifecycle.Event.ON_STOP.equals(event)) {
                    mKeyToCallback.remove(key);
                } else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
                    unregister(key);
                }
            }
        };
        lifecycleContainer.addObserver(observer);
        mKeyToLifecycleContainers.put(key, lifecycleContainer);
    
        //5、初始化ActivityResultLauncher对象并返回
        return new ActivityResultLauncher<I>() {
            @Override
            public void launch(I input, @Nullable ActivityOptionsCompat options) {
                mLaunchedKeys.add(key);
                onLaunch(requestCode, contract, input, options);
            }

            @Override
            public void unregister() {
                ActivityResultRegistry.this.unregister(key);
            }

            @NonNull
            @Override
            public ActivityResultContract<I, ?> getContract() {
                return contract;
            }
        };
    }
  • 1处判断当前Lifecycle的状态必须小于Lifecycle.State.STARTED,否则直接抛异常。也就是说registerForActivityResult()必须在onStart()之前调用
  • 2处注册键值,该方法会调用registerKey()方法生成一个唯一的requestCode,并将keyrequestCode绑定起来。
  • 3处从mKeyToLifecycleContainers集合中获取与key对应的LifecycleContainer对象。如果不存在,则会创建一个新的LifecycleContainer对象,并将其与lifecycle绑定起来。
  • 4处创建一个LifecycleEventObserver对象,并将其添加到3处的LifecycleContainer对象中。该观察者会在LifecycleOwner对象的生命周期发生变化时执行相应的操作,例如在STARTED状态时将CallbackAndContract对象添加到mKeyToCallback集合中,在STOPPED状态时将其从集合中移除,在DESTROYED状态时调用unregister()方法注销ActivityResultLauncher对象等。
  • 5处返回一个ActivityResultLauncher对象,该对象包含了launch()unregister()getContract()三个方法。其中,launch()方法用于启动ActivityIntent操作,unregister()方法用于注销ActivityResultLauncher对象,getContract()方法用于获取与之绑定的ActivityResultContract对象。

ActivityResultLauncher#launch()

上一节中registerForActivityResult()返回了ActivityResultLauncher对象,当启动跳转时,只需要调用launch()方法即可:

public void launch(I input) {
   launch(input, null);
}

public abstract void launch( I input, ActivityOptionsCompat options);

可以看到最终调用的 launch( I input, ActivityOptionsCompat options) 方法是一个abstract 抽象方法,其具体实现自然是上一节5处初始化ActivityResultLauncher的地方,launch()内部又调用了ActivityResultRegistry#onLaunch(requestCode, contract, input, options) 方法,继续找ActivityResultRegistry初始化的地方,可以找到在ComponentActivity中初始化了ActivityResultRegistry(这里看的是androidx.activity:activity:1.3.1版本):

 this.mActivityResultRegistry = new ActivityResultRegistry() {
       public <I, O> void onLaunch(final int requestCode, @NonNull ActivityResultContract<I, O> contract, I input, @Nullable ActivityOptionsCompat options) {
                ComponentActivity activity = ComponentActivity.this;
                final ActivityResultContract.SynchronousResult<O> synchronousResult = contract.getSynchronousResult(activity, input);
                //1、同步检查结果,如果不为空,直接返回contract.getSynchronousResult中的数据
                if (synchronousResult != null) {
                    (new Handler(Looper.getMainLooper())).post(new Runnable() {
                        public void run() {
                            dispatchResult(requestCode, synchronousResult.getValue());
                        }
                    });
                } else {
                //2、创建Intent对象
                    Intent intent = contract.createIntent(activity, input);
                    Bundle optionsBundle = null;
                    if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
                        intent.setExtrasClassLoader(activity.getClassLoader());
                    }

                    if (intent.hasExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE")) {
                        optionsBundle = intent.getBundleExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
                        intent.removeExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
                    } else if (options != null) {
                        optionsBundle = options.toBundle();
                    }

                    if ("androidx.activity.result.contract.action.REQUEST_PERMISSIONS".equals(intent.getAction())) {
                        String[] permissions = intent.getStringArrayExtra("androidx.activity.result.contract.extra.PERMISSIONS");
                        if (permissions == null) {
                            permissions = new String[0];
                        }

                        ActivityCompat.requestPermissions(activity, permissions, requestCode);
                    } else if ("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST".equals(intent.getAction())) {
                        IntentSenderRequest request = (IntentSenderRequest)intent.getParcelableExtra("androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST");

                        try {
                            ActivityCompat.startIntentSenderForResult(activity, request.getIntentSender(), requestCode, request.getFillInIntent(), request.getFlagsMask(), request.getFlagsValues(), 0, optionsBundle);
                        } catch (final IntentSender.SendIntentException var11) {
                            (new Handler(Looper.getMainLooper())).post(new Runnable() {
                                public void run() {
                                    dispatchResult(requestCode, 0, (new Intent()).setAction("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST").putExtra("androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION", var11));
                                }
                            });
                        }
                    } else {
                        ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
                    }

                }
            }
        };
  • 1处用ActivityResultContractgetSynchronousResult()方法获取同步结果。如果存在同步结果,则会将其返回值通过dispatchResult()方法分发出去。
  • 如果1处不存在同步结果,那么就会走2处逻辑,调用ActivityResultContractcreateIntent()方法创建一个新的Intent对象,并根据需要设置ActivityIntent操作的选项。
  • 检查Intent对象的Action属性是否为"androidx.activity.result.contract.action.REQUEST_PERMISSIONS""androidx.activity.result.contract.action.INTENT_SENDER_REQUEST"。如果是,则会分别调用ActivityCompat.requestPermissions()方法请求权限或ActivityCompat.startIntentSenderForResult()方法启动相应的操作;如果都不是,则默认调用ActivityCompat.startActivityForResult()方法启动ActivityIntent操作。

可以看到最终还是调用的startActivityForResult()/requestPermissions()这些方法发起了请求,那么请求完的结果也会返回到onActivityResult()/onRequestPermissionsResult()中:

    //ComponentActivity.java
    //1、startActivityForResult()返回结果
    @Deprecated
    @CallSuper
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!this.mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    //2、权限申请的结果
    @Deprecated
    @CallSuper
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (!this.mActivityResultRegistry.dispatchResult(requestCode, -1, (new Intent()).putExtra("androidx.activity.result.contract.extra.PERMISSIONS", permissions).putExtra("androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS", grantResults)) && VERSION.SDK_INT >= 23) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
  
     //分发ActivityResultLauncher对象的返回结果
    @MainThread
    public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
        String key = mRcToKey.get(requestCode);
        if (key == null) {
            return false;
        }
        mLaunchedKeys.remove(key);

        doDispatch(key, resultCode, data, mKeyToCallback.get(key));
        return true;
    }

    //3、处理返回结果
    private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
            @Nullable CallbackAndContract<O> callbackAndContract) {
        if (callbackAndContract != null && callbackAndContract.mCallback != null) {
            ActivityResultCallback<O> callback = callbackAndContract.mCallback;
            ActivityResultContract<?, O> contract = callbackAndContract.mContract;
            callback.onActivityResult(contract.parseResult(resultCode, data));
        } else {
            // Remove any parsed pending result
            mParsedPendingResults.remove(key);
            // And add these pending results in their place
            mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
        }
    }

主要来看3处的doDispatch()方法:

  • 检查callbackAndContract对象是否为空,如果不为空,则会调用ActivityResultCallback对象的onActivityResult()方法处理返回结果。到这里执行结果就会在registerForActivityResult()中的第2个参数中收到结果
  • 如果上述不成立,会将返回结果存储到mPendingResults集合中,以便稍后使用。

总结

  • 个人认为startActivityForResult()/onActivityResult()、onRequestPermissionsResult()被标记成过时并不是因为有什么性能问题或者bug,而是通过registerForActivityResult()统一进行收口,为什么这么说呢,因为registerForActivityResult()内部最终还是调用了ComponentActivitystartActivityForResult()/onActivityResult()、onRequestPermissionsResult()方法,只不过是对其进行了统一的封装而已,简化了使用。
  • 使用 registerForActivityResult(),可以通过一个 lambda 表达式来处理返回结果,避免了繁琐的 requestCodeonActivityResult 的处理逻辑。此外,registerForActivityResult 还支持多个请求码和多个返回结果,使得代码更加清晰和易于维护。

资料

【1】android官网:Activity Result API

欢迎扫描下方二维码或搜索微信公众号 代码说, 关注我的微信公众号查看最新文章~