学点什么之 startActivityForResult 的新用法

简单介绍

startActivityForResult 是用来启动另一个 activity 后接收回调的,这个方法在 activity 和 fragment 里面都可以调用。使用后在调用者的 onActivityResult 里面进行返回参数的处理即可。这样不可避免导致了调用逻辑和处理逻辑的分离,为我们代码的可阅读性增加了几分难度。

Android 官方应该也是注意到了这个问题,在 AndroidX 中为我们提供了 Activity Result API 功能,封装了 startActivityForResult 的相关操作,那么我们就来实践一下这个新的接口吧。

代码操作

按照官方的说明,已经为我们提供了拍照、选择文件等操作的封装,也可以自定义处理相关类。我们就直接自定义来处理,我们的需求就是第一个页面传送分数到第二个页面,第二个页面处理后返回分数的等级是否优秀。这样我们入参就是 Int 型,出参就是 String 型的,我们定义下我们的协议类TestContract

class TestContract : ActivityResultContract<Int, String>() {
    override fun createIntent(context: Context, content: Int): Intent {
        val intent = Intent(context, SecondActivity::class.java)
        intent.putExtra("score", content)
        return intent
    }


    override fun parseResult(resultCode: Int, result: Intent?): String {
        if (resultCode != Activity.RESULT_OK) {
            return ""
        }
        return result?.getStringExtra("level") ?: ""
    }
}

我们看一下第一个界面的调用方法和第二个界面的处理方法。

// 构建我们的协议对象
val resultLauncher = registerForActivityResult(TestContract()) { result: String ->
    Log.d(TAG, "接收到数据 $result")
}
// 调用协议对象
btnFirst.setOnClickListener { resultLauncher.launch(80) }

// 第二个界面中的处理逻辑
val score = intent.extras!!.getInt("score")
btnBack = findViewById(R.id.btnBack)
btnBack.setOnClickListener(View.OnClickListener {
    val intent = Intent()
    if (score > 90) {
        intent.putExtra("level", "优秀")
    } else {
        intent.putExtra("level", "不优秀")
    }
    setResult(RESULT_OK, intent)
    finish()
}) 

可以看到第二个界面的处理逻辑和之前写法没有什么区别,只是在第一个界面调用的时候,我们通过registerForActivityResult注册我们的处理对象,通过launch()方法调用。

看看实现

我们就按照我们上面定义的最简单版本来看一下 AndroidX 是如何帮我们实现的吧。

我们调用registerForActivityResult方法时,会调用到ActivityResultRegistryregister方法,这里面会使用ComponentActivity里的mActivityResultRegistry,里面定义了启动方法,我们也可以自定义该对象来进行测试。

    @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);
    }

register会构造出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;
            }
        };

接着看看调用时的launch方法,这个方法会调用到mActivityResultRegistryonLaunch方法。

    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;
            // ......
            // 调用我们的协议类获取 intent
            Intent intent = contract.createIntent(activity, input);
            Bundle optionsBundle = null;
            // 设置 ClassLoader
            if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
                intent.setExtrasClassLoader(activity.getClassLoader());
            } 
            if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) {
              // 内置的请求权限处理
            } else if (ACTION_INTENT_SENDER_REQUEST.equals(intent.getAction())) {
              // 内置的 IntentSenderRequest 处理
            } else {
                // startActivityForResult path
                ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
            }
        }
    };

到这里调用了具体的方法。

到这里我们把具体的发送调用分析的差不多了,对应的还有回调事件,我们找到ComponentActivityonActivityResult方法。

    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

调用了dispatchResult方法,里面调用了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));
        }
    }

我们看上半部分,获取到了callbackcontract然后通过我们的协议类解析数据,就是调用了我们的TestContract里面的parseResult方法。然后将结果返回给registerForActivityResult时传入的callback即可。

这样就完成了整个操作,可见发现这里面是官方在ComponentActivity层给我们封装了操作,封装的代码可以发现考虑了各种情况,通过我们自定义协议类将Intent的构造和回调数据的解析分离出去,我个人感觉提高了代码的可读性。在平时的开发过程中获取我们也多学习学习这种想法,从而改造我们的代码,让我们的代码更加优雅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值