深入解析 registerForActivityResult

本文详细解析了Android中registerForActivityResult的使用,包括ActivityResultContract、ActivityResultCallback、ActivityResultLauncher等关键组件的工作原理,以及ActivityResultRegistry如何协调整个流程。通过实例代码和流程分析,阐述了这一新API如何替代startActivityForResult,帮助开发者更好地理解和运用。

深入解析 registerForActivityResult

简介

  • registerForActivityResult方法是官方推出的用于在ComponetActivity和Fragment中替代startActivityForResult方法的新api
  • 官方描述:https://developer.android.google.cn/training/basics/intents/result#kotlin

解析

  • 话不多说,我们先上一段官方代码
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
    // Handle the returned Uri
}

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    val selectButton = findViewById<Button>(R.id.select_button)

    selectButton.setOnClickListener {
        // Pass in the mime type you'd like to allow the user to select
        // as the input
        getContent.launch("image/*")
    }
}
  • 这段代码实现了点击按钮跳转到选择图片的文件选择器界面并获取选择结果的功能。接下来就让我们来看看这段代码是怎么在 ComponetActivity 中实现这样的功能的。
  • 在我们看代码之前,需要始终记住一点,那就是不管 registerForActivityResult 方法中的逻辑如何封装,功能的最终实现本质还是在 ComponentActivity 中构建一个相应的 Intent 对象传入其 startActivityForResult 方法中,然后在其 onActivityResult 回调中获取结果。

ActivityResultContract

  • 首先我们看到在 registerForActivityResult 方法中传入了两个参数,一个是 GetContent() ,一个是Lambda表达式。那么这个GetContent()到底是个啥?话不多说,上代码。
public final class ActivityResultContracts {
    private ActivityResultContracts() {}

    // ...

    public static class GetContent extends ActivityResultContract<String, Uri> {

        @CallSuper
        @NonNull
        @Override
        public Intent createIntent(@NonNull Context context, @NonNull String input) {
            return new Intent(Intent.ACTION_GET_CONTENT)
                    .addCategory(Intent.CATEGORY_OPENABLE)
                    .setType(input);
        }

        @Nullable
        @Override
        public final SynchronousResult<Uri> getSynchronousResult(@NonNull Context context,
                @NonNull String input) {
            return null;
        }

        @Nullable
        @Override
        public final Uri parseResult(int resultCode, @Nullable Intent intent) {
            if (intent == null || resultCode != Activity.RESULT_OK) return null;
            return intent.getData();
        }
    }

    // ...

}
  • 可以看到 GetContent 是抽象类 ActivityResultContract< I,O > 的子类,我们来简要介绍下他重写的三个方法
  • creatIntent 方法用于创建 startActivityForResult 方法所需的 Intent 对象
  • getSynchronousResult 方法若返回值不为 null 会直接将他的返回值作为结果而不调用 startActivityForResult 方法
  • parseResult 方法用于对从 onActivityResult 回调或 getSynchronousResult 方法中得到的此次结果进行处理
  • ActivityResultContracts 类中是官方为各种不同用途事先提供好的 ActivityResultContract 子类定义,如果要实现某些特殊的功能也可以自己写一个 ActivityResultContract 的子类

ActivityResultCallback< O >

  • 这就是我们传入 registerForActivityResult 方法的第二个参数(Lambda),一个只有一个方法的接口,当最终结果被 parseResult 方法处理后会传入他的 onActivityResult 回调中,还是给段代码看下
public interface ActivityResultCallback<O> {
    void onActivityResult(@SuppressLint("UnknownNullness") O result);
}

ComponentActivity().registerForActivityResult

  • 我们已经了解了传入的参数大致是什么,现在再来看看 ComponentActivity 的 registerForActivityResult 方法中到底做了什么,话不多说,上代码
// ...
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);
    }

public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    }
// ...
  • 可以看到实际调用的是 mActivityResultRegistry 的 register 方法
  • 我们首先来看看这个 register 方法的返回值是个啥

ActivityResultLauncher< I >

  • 话不多说,上代码
public abstract class ActivityResultLauncher<I> {

    public void launch(@SuppressLint("UnknownNullness") I input) {
        launch(input, null);
    }

    public abstract void launch(@SuppressLint("UnknownNullness") I input,
            @Nullable ActivityOptionsCompat options);

    // ...
}
  • 这么看可能还不太清晰,我们再把 GetContent 的父类 ActivityResultContract< I, O > 的代码贴上来对比看看
public abstract class ActivityResultContract<I, O> {

    public abstract @NonNull Intent createIntent(@NonNull Context context,
            @SuppressLint("UnknownNullness") I input);

    // ...
}
  • 再结合上面的 registerForActivityResult 方法代码,我们可以发现 launch 方法和 createIntent 方法的 input 参数是同一个类型!现在我们再来回忆下那段官方代码
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
    // Handle the returned Uri
}

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    val selectButton = findViewById<Button>(R.id.select_button)

    selectButton.setOnClickListener {
        getContent.launch("image/*")
    }
}
  • 结合上面,我们已经可以大致猜测出内部运行逻辑了
  • 首先我们向 registerForActivityResult 传入了我们自定义的 ActivityResultContract< I,O > 子类对象(此处为 GetContent() )和 ActivityResultCallback< O > (后面的Lambda),registerForActivityResult 返回一个 ActivityResultLauncher< I > 对象(getContent)
  • 当我们需要执行选择图像的操作时,调用 getContent 的 lauch 方法并传入 “image/*” 参数
  • 这个参数又以某种方式传递给了之前 GetContent() 的 createIntent 方法中的 input 参数,于是构建出了一个 Intent 对象
  • 因为 GetContent() 的 SynchronousResult 方法返回 null ,所以这个 Intent 对象通过某种方式被用于 startActivityForResult 中
  • 终于,用户结束了选择,选择的结果以Intent的形式被传入 ComponentActivity 的 onActivityResult 回调中,而这个结果又以某种方式被传入了之前 GetContent() 的 parseResult 方法中得到了处理后的结果
  • 现在,我们再来看两段熟悉的代码,结合 registerForActivityResult 代码有没有想到什么
public abstract class ActivityResultContract<I, O> {    
    // ...

    @SuppressLint("UnknownNullness")
    public abstract O parseResult(int resultCode, @Nullable Intent intent);

    // ...
}
public interface ActivityResultCallback<O> {
    void onActivityResult(@SuppressLint("UnknownNullness") O result);
}
  • 没错, parseResult 方法的返回类型正是 ActivityResultCallback< O > 接口的 onActivityResult 回调接受参数的类型!
  • 这意味着在我们得到了通过之前 GetContent() 的 parseResult 处理后的结果后(即选择的图片的 Uri 或 null ),他又被某种方式传递给了 Lambda 的 uri 参数
  • 接着我们便可以在 Lambda 中对最终结果进行我们要进行的操作了(例如上传图片)
  • 现在我们可以来小小的总结下各个类的作用:
    • ActivityResultContract:我们需要使用官方提供或自定义的子类为以上过程提供特定的 Intent 对象(createIntent),处理返回的结果(parseResult)以及决定是否不执行以上流程直接返回一个相应结果(SynchronousResult)。他就像契约一样对整个过程中的重要节点做出了约束。
    • ActivityResultCallback< O >:我们需要在其中定义获得经处理的结果的后续操作,这相当于以前使用 startActivityForResult 后在 onActivityResult 回调中的后续操作,但是使用他便可以与 ComponentActivity 解耦。
    • ActivityResultLauncher< I >:由调用 registerForActivityResult 方法获得,调用其 lauch 方法以启动以上整个流程

ActivityResultRegistry

  • 到此为止,我们已经可以在日常开发中使用 registerForActivityResult 方法了,但既然是完全解析,那么我们还要接着往下看
  • 在上面的流程中,某种方式就像粘合剂一样将整个流程中的各个步骤粘合在了一起,而实现某种方式的正是 ActivityResultRegistry,废话少说,上代码
public abstract class ActivityResultRegistry {
    
    // ...

    @MainThread
    public abstract <I, O> void onLaunch(
            int requestCode,
            @NonNull ActivityResultContract<I, O> contract,
            @SuppressLint("UnknownNullness") I input,
            @Nullable ActivityOptionsCompat options);

    @NonNull
    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) {

        // ...
    }

    @MainThread
    final void unregister(@NonNull String key) {
        
        // ...
    }

    @MainThread
    public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
        
        // ...
    }

    @MainThread
    public final <O> boolean dispatchResult(int requestCode,
            @SuppressLint("UnknownNullness") O result) {
        // ...
    }

    private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
            @Nullable CallbackAndContract<O> callbackAndContract) {
        
        // ...
    }

    // ...
}
  • 不愧是粘合剂,重要方法都比别人多,不要紧,我们一条条来看

register 方法

  • 不知各位是否还记得, registerForActivityResult 方法实际上调用的就是 ActivityResultRegistry 的 register 方法,那我们就从这看起,话不多说,上代码
    @NonNull
    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();

        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.");
        } // 1.

        final int requestCode = registerKey(key);
        LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
        if (lifecycleContainer == null) {
            lifecycleContainer = new LifecycleContainer(lifecycle);
        }
        LifecycleEventObserver observer = new LifecycleEventObserver() { // 2.
            @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)) {
                        @SuppressWarnings("unchecked")
                        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);

        return new ActivityResultLauncher<I>() {
            @Override
            public void launch(I input, @Nullable ActivityOptionsCompat options) {
                mLaunchedKeys.add(key);
                Integer innerCode = mKeyToRc.get(key);
                onLaunch((innerCode != null) ? innerCode : requestCode, contract, input, options); // 3.
            }

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

            @NonNull
            @Override
            public ActivityResultContract<I, ?> getContract() {
                return contract;
            }
        };
    }
  • 代码较长,我们挑重点来理解
    1. 可以看到 ActivityResultRegistry 对于对应的 ComponentActivity 的 lifeCycle 进行了判断,这意味着我们只能在 ComponentActivity 处于 onStart 之前调用 registerForActivityResult 方法进行 register 操作,否则会抛出异常
    2. 可以看到 ActivityResultRegistry 加入对于 ComponentActivity 的 lifeCycle 的观察,保证只有当此 ComponentActivity 处于可用状态时才会执行回调,不可用时自动注销
    3. 注册完毕后返回一个 ActivityResultLauncher 对象,可以看到我们传入其 launch 方法的 input 参数又传给了 ActivityResultRegistry 的 onLauch 方法

onLaunch 方法

  • 话不多说,上代码
    @MainThread
    public abstract <I, O> void onLaunch(
            int requestCode,
            @NonNull ActivityResultContract<I, O> contract,
            @SuppressLint("UnknownNullness") I input,
            @Nullable ActivityOptionsCompat options);
  • 可以看到, onLaunch 是一个抽象方法,这时我们又想起了一段熟悉的代码
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    }
  • 这个 mActivityResultRegistry 不正是 ComponentActivity 持有的 ActivityResultRegistry 的实现类吗,让我们来看看他是怎么来定义 onLaunch 的,来人啊,上代码
    private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {

        @Override
        public <I, O> void onLaunch(
                final int requestCode,
                @NonNull ActivityResultContract<I, O> contract,
                I input,
                @Nullable ActivityOptionsCompat options) {
            ComponentActivity activity = ComponentActivity.this;

            // Immediate result path
            final ActivityResultContract.SynchronousResult<O> synchronousResult =
                    contract.getSynchronousResult(activity, input);  // 1.
            if (synchronousResult != null) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        dispatchResult(requestCode, synchronousResult.getValue()); // 5.
                    }
                });
                return;
            }

            // Start activity path
            Intent intent = contract.createIntent(activity, input); // 2.
            Bundle optionsBundle = null;
            // If there are any extras, we should defensively set the classLoader
            if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
                intent.setExtrasClassLoader(activity.getClassLoader());
            }
            if (intent.hasExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE)) {
                optionsBundle = intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE);
                intent.removeExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE);
            } else if (options != null) {
                optionsBundle = options.toBundle();
            }
            if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) {

                // requestPermissions path
                String[] permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS);

                if (permissions == null) {
                    permissions = new String[0];
                }

                ActivityCompat.requestPermissions(activity, permissions, requestCode); // 3.
            } else if (ACTION_INTENT_SENDER_REQUEST.equals(intent.getAction())) {
                IntentSenderRequest request =
                        intent.getParcelableExtra(EXTRA_INTENT_SENDER_REQUEST);
                try {
                    // startIntentSenderForResult path
                    ActivityCompat.startIntentSenderForResult(activity, request.getIntentSender(),
                            requestCode, request.getFillInIntent(), request.getFlagsMask(),
                            request.getFlagsValues(), 0, optionsBundle);
                } catch (final IntentSender.SendIntentException e) {
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            dispatchResult(requestCode, RESULT_CANCELED,
                                    new Intent().setAction(ACTION_INTENT_SENDER_REQUEST)
                                            .putExtra(EXTRA_SEND_INTENT_EXCEPTION, e));
                        }
                    });
                }
            } else {
                // startActivityForResult path
                ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle); // 4.
            }
        }
    };
  • 终于,在这里看到了许许多多熟悉的面孔,1~4就不再介绍了,他们正如我们之前所猜想的流程那样定义在 onLaunch 方法中
  • 我们注意到 getSynchronousResult 的结果是通过 ActivityResultRegistry 的 dispatchResult 方法传递出去的,那么这个方法是干啥的

dispatchResult 方法

  • 在此之前,我们先来看看 ComponentActivity 中的 onActivityResult 回调
    @CallSuper
    @Override
    @Deprecated
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
  • 可以看到他也用了 dispatchResult 方法将结果传递出去,现在我们再来看 dispatchResult 的代码
    @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;
    }
  • 我们可以看到,如果是没通过 registerForActivityResult 方法注册过的操作结果那么就让他接着回到 ComponentActivity 父类的 onActivityResult 回调,否则将结果传入 doDispatch 方法中

doDispatch 方法

  • 终于,终于到头了,什么?你听不懂?好,看代码!
    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));
        }
    }
  • 和我们设想的一样,最终注册的操作的结果进入了 doDispatch 方法中,经由 parseResult 方法处理后传入了 callback.onActivityResult 回调(我们的Lambda表达式),就这样我们走完了一整个流程

其他

  • 细心的你会发现,在上面 mActivityResultRegistry 的 onLaunch 方法的 注释5. 处调用的并不是上面的 dispatchResult 方法,而是另一个重载方法
    @MainThread
    public final <O> boolean dispatchResult(int requestCode,
            @SuppressLint("UnknownNullness") O result) {
        String key = mRcToKey.get(requestCode);
        if (key == null) {
            return false;
        }
        mLaunchedKeys.remove(key);

        CallbackAndContract<?> callbackAndContract = mKeyToCallback.get(key);
        if (callbackAndContract == null || callbackAndContract.mCallback == null) {
            // Remove any pending result
            mPendingResults.remove(key);
            // And add these pre-parsed pending results in their place
            mParsedPendingResults.put(key, result);
        } else {
            @SuppressWarnings("unchecked")
            ActivityResultCallback<O> callback =
                    (ActivityResultCallback<O>) callbackAndContract.mCallback;
            callback.onActivityResult(result);
        }
        return true;
    }
  • 看来逻辑和另一个 dispatchResult 方法差不多,不过这里就不用再调用 doDispatch 方法了,直接回调 callback.onActivityResult
  • 细心的你又发现, ActivityResultRegistry 还有另一个 register 方法
    @NonNull
    public final <I, O> ActivityResultLauncher<I> register(
            @NonNull final String key,
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultCallback<O> callback)
  • 我们可以看到,他不需要 LifecycleOwner 参数,这意味着我们可以在任何时刻调用这个 register 方法,但也意味着我们需要手动对我们 register 的操作进行 unregister

至此,我们就已经了解了 registerForActivityResult 中的绝大部分重要内容了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值