Activity Result API 使用与源码分析

Posted 冬天的毛毛雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity Result API 使用与源码分析相关的知识,希望对你有一定的参考价值。

好文推荐:
作者:wang_zd

一,介绍和使用

1.介绍

Activity Result API 提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。 用来代替startActivityForResult()onActivityResult()。Google已经在新版的androidx activity和fragment中将这两个方法置为Deprecated,以后推荐使用的则是这套新的API官方文档-获取 activity 的结果

2.启动其它Activity并返回结果

使用registerForActivityResult方法注册一个启动项,第一个参数是内置的协定StartActivityForResult,用来启动Activity并返回结果。第二个参数是ActivityResultCallback,处理结果的回调,相当于以前onActivityResult方法。该方法返回结果是一个ActivityResultLauncher,在需要的地方调用它的launch方法即可。

var start=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
    if(it.resultCode==Activity.RESULT_OK){
        var result=it.data?.getStringExtra("result")
        binding.tv.text="result:${result}"
    }
}
binding.btn.setOnClickListener {
    val intent=Intent(this,MainActivity14::class.java).apply {
        putExtra("text","hello MainActivity14")
    }
    start.launch(intent)
}
3.权限申请

它还可以用来做权限申请,使用RequestPermission协定,在launch方法中传入需要申请的权限,如果用户授权则会在回调中返回ture。还可以使用RequestMultiplePermissions来申请多个权限,相比以前的权限申请真的简单很多。

val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
    log("permission:$it")
}
binding.btn.setOnClickListener {
    requestPermission.launch(android.Manifest.permission.ACCESS_FINE_LOCATION)
}
4.其它内置ActivityResultContract

ActivityResultContracts中内置了一些基本的协定。比如启动Activity,拍照返回结果,选联系人,请求权限等。用法类似,只是请求的参数和返回的结果不同。

5.自定义ActivityResultContract

如果系统内置协定不满足需求,可以通过继承ActivityResultContract类来自定义协定,ActivityResultContract有两个范型参数,InputOutput。然后现实它的两个方法,createIntent方法来创建你的意图IntentparseResult方法通过resultCodeintent来解析结果并返回Output类型。 类似StartActivityForResult源码

public static final class StartActivityForResult
        extends ActivityResultContract<Intent, ActivityResult> {

    public Intent createIntent(@NonNull Context context, @NonNull Intent input) {
        return input;
    }

    public ActivityResult parseResult(
            int resultCode, @Nullable Intent intent) {
        return new ActivityResult(resultCode, intent);
    }
}
6.在其它类中接收Activity的结果

需要通过实现LifecycleObserver来实现协定的注册和启动器启动。

Activity注册这个观察者
var myLifecycleObserver=MyLifecycleObserver(registry = activityResultRegistry)
lifecycle.addObserver(myLifecycleObserver)
调用
myLifecycleObserver.selectImage()

实现DefaultLifecycleObserver,在onCreate注册协定
class MyLifecycleObserver(private val registry : ActivityResultRegistry) :DefaultLifecycleObserver{
    lateinit var getContent : ActivityResultLauncher<String>
    override fun onCreate(owner: LifecycleOwner) {
        getContent = registry.register("key", owner, ActivityResultContracts.GetContent(),
            ActivityResultCallback { log(it) })
    }
    fun selectImage(){
        getContent.launch("image/*")
    }
}

二,源码

使用版本activity-ktx:1.2.2,fragment-ktx:1.3.3

1.register

registerForActivityResult方法最终会调用ActivityResultRegistryregister方法,通过不同的key生成一个唯一的requestCode,把这个requestCode保存下来,在结果返回时使用。通过Lifecycle添加一个观察者,监听Activity的ON_START,ON_STOP,ON_DESTROY事件。在ON_START事件到来时,会从mPendingResults这个bundle对象中通过key获取结果,如果有结果,则将结果回调到调用者处。ON_STOP事件则是移除这个key对应的CallbackAndContract对象,ON_DESTROY事件移除这个key对应的所有信息。最后返回ActivityResultLauncher对象给调用者使用。

    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();
        //每个请求的key也不一样,根据key生成一个唯一的requestCode
        final int requestCode = registerKey(key);
        //把lifecycle和observer统一封装到LifecycleContainer中
        LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
        if (lifecycleContainer == null) {
            lifecycleContainer = new LifecycleContainer(lifecycle);
        }
        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)) {
                        @SuppressWarnings("unchecked")
                        final O parsedPendingResult = (O) mParsedPendingResults.get(key);
                        mParsedPendingResults.remove(key);
                        callback.onActivityResult(parsedPendingResult);
                    }
                    //从bundle中获取结果
                    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);//stop时移除
                } else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
                    unregister(key);//destory时注销
                }
            }
        };
        //添加观察者
        lifecycleContainer.addObserver(observer);
        mKeyToLifecycleContainers.put(key, lifecycleContainer);
       //返回ActivityResultLauncher对象
        return new ActivityResultLauncher<I>() {
            @Override
            public void launch(I input, @Nullable ActivityOptionsCompat options) {
                mLaunchedKeys.add(key);
                //ActivityResultRegistry的onLaunch,抽象方法,在ComponentActivity内实现
                onLaunch(requestCode, contract, input, options);
            }
            public void unregister() {
                ActivityResultRegistry.this.unregister(key);
            }
            public ActivityResultContract<I, ?> getContract() {
                return contract;
            }
        };
    }
2.launch

ActivityResultRegistryonLaunch是一个抽象方法,在ComponentActivity内实现。首先会检查contract是否能获取同步结果,如果能,则直接分发处理。比如权限申请,我前面已经获取了权限,直接可以通过getSynchronousResult查询是否有权限。有直接返回true,不再通过requestPermissions去做申请权限。

在通过contract获取Intent,按Intent分为三种情况处理,第一种,权限申请,通过requestPermissions去申请权限,第二种处理INTENT_SENDER,和startIntentSenderForResult类似。第三种,处理其它问题,通过startActivityForResult启动Activity

        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);
            if (synchronousResult != null) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        dispatchResult(requestCode, synchronousResult.getValue());
                    }
                });
                return;
            }

            // 根据协定创建Intent对象
            Intent intent = contract.createIntent(activity, input);
            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);
            } else if (ACTION_INTENT_SENDER_REQUEST.equals(intent.getAction())) {
            //处理StartIntentSenderForResult
                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) {

                }
            } else {
                //其它普通问题启动Activity
                ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
            }
        }
3.结果回调

ComponentActivity中,onActivityResult和onRequestPermissionsResult方法会先被ActivityResultRegistry的dispatchResult方法拦截,如果它能处理,则不再调用Activity的方法。

dispatchResult会调用doDispatch来处理结果,这有两种情况,通过keymKeyToCallback中获取callbackAndContract对象,如果不为空,则直接回调结果。比如权限申请,不会走ON_STOP事件,mKeyToCallback不会被清除,这里就不会为空。如果为空则把结果放到mPendingResults这个Bundle对象中,和前面的ON_START事件对应起来,从mPendingResults取出结果,回调结果。

    -ComponentActivity.java
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
   public void onRequestPermissionsResult(
            int requestCode,
            @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, Activity.RESULT_OK, new Intent()
                .putExtra(EXTRA_PERMISSIONS, permissions)
                .putExtra(EXTRA_PERMISSION_GRANT_RESULTS, grantResults))) {
            if (Build.VERSION.SDK_INT >= 23) {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }
- ActivityResultRegistry.java
 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;
    }
 private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
            @Nullable CallbackAndContract<O> callbackAndContract) {
       callbackAndContract不为空则直接回调即可
        if (callbackAndContract != null && callbackAndContract.mCallback != null) {
            ActivityResultCallback<O> callback = callbackAndContract.mCallback;
            ActivityResultContract<?, O> contract = callbackAndContract.mContract;
            callback.onActivityResult(contract.parseResult(resultCode, data));
        } else {把结果放到mPendingResults中。等会调用。
            // Remove any parsed pending result
            mParsedPendingResults.remove(key);
            // And add these pending results in their place
            mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
        }
    }
4.如果启动其它Activity,当前Activity被回收怎么办

在 ComponentActivity中的onSaveInstanceState和onCreate方法中对ActivityResultRegistry的数据做了保存和恢复处理。

最后

大家如果还想了解更多Android 相关的更多知识点,可以点进我的GitHub项目中:https://github.com/733gh/GH-Android-Review-master自行查看,里面记录了许多的Android 知识点。最后还请大家点点赞支持下!!!

以上是关于Activity Result API 使用与源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Activity Result API 动态申请Android权限

如何在 Activity Result API 中设置请求代码?

startActivityForResult废弃了,用Activity Result API吧

Activity Result API详解,是时候放弃startActivityForResult了

Activity Result API详解,是时候放弃startActivityForResult了

Activity Result API详解,是时候放弃startActivityForResult了