搞懂 Activity Result API (一)
「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
1、常规用法
当我们进行 Android 开发时,必然要用到的一个操作姿势:从 Activity A 启动 另一个 Activity B ,并 接收 Activity B 的返回结果。那我们的开发姿势可能是这样滴:
-
启动另一个 Activity B:
startActivityForResult(new Intent(ActivityA.this, ActivityB.this),requestCode)或者使用 ARouter
ARouter.getInstance().build("/app/activity_b").navigation(mContext, requestCode) -
接收 Activity B 的返回结果:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) {
return;
}
if (requestCode == xxx) { // 1
...
}
}
上面的操作姿势挺好的呀,有啥问题呢?试想一下,如果 Activity A 的业务比较复杂,它的界面和用户有许多不同的操作互动,需要启动多个 Activity , 获取多个 Activity 的返回结果,那上述 注释1 的姿势就变成下面这样色的啦:
if (requestCode == 1) {// 2
...
} else if (requestCode == 2) {
...
} else if ...
上面注释2,是我们根据 requestCode 来区分不同的 Activity 返回结果,进而处理我们的逻辑,那如果我们处理的逻辑比较复杂,那这个 onActivityResult 就比较复杂啦,会有大量的if ... else...。
此时,有一个朋友站起来说,这个也不算是啥痛点呀,我们的页面没有那么复杂,或者说我们可以接受这样的大量的if ... else... 来处理逻辑。
这位朋友别着急,你先坐下,我们在继续分析,看一下上面的方式还有啥不那么友好的地方。
咦,定睛一看,我们发现无论是 启动过程 , 还是 接收过程,都需要 requestCode 参与进去。启动 Activity 的时候,我们为其 Activity 指定一个 requestCode 来标识,在接收该 Activity 的结果时,使用这个 requestCode 来区分是哪一个 Activity 。一般情况下,项目中不是一个人开发,那我们就需要维护一个 requestCode 避免同组的小伙伴起相同啦,害。
此时,又有一个朋友站起来了说,维护个 requestCode 也不算啥痛点呀。
这位朋友你说的对,你别坐下啦,请这位朋友向前10步走,然后左拐,出去!!!
好了,在座的朋友们都是身有其感的,那我们继续搞懂下 Activity Result API
2、使用 Activity Result API
我们用 Activity Result API 的方式来走一遍上面的过程:
-
启动另一个 Activity B:
-
首先
new一个约束ActivityResultContracts.StartActivityForReult, 这个约束使启动活动的输入是Intent, 输出是ActivityResult,后续会解释这个输入和输出的取值依据。ActivityResultContracts.StartActivityForResult contract = new ActivityResultContracts.StartActivityForResult(); -
然后嘞,再
new一个 接收Activity B结果的回调ActivityResultCallback<ActivityResult> callBack = new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { ... //3 } }; -
再然后嘞,搞一个启动
Activity B的启动器:将前面的 约束 和 结果回调 传入到registerForActivityResult方法中。private ActivityResultLauncher<Intent> mActivityBLauncher = registerForActivityResult(contract, callBack); -
最后,启动啦:例如在点击事件中,调用下面方法,即可启动一个 Activity B:
mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this));
-
-
接收 Activity B 的返回结果:
在上面启动的时候,我们
new了一个 接收Activity B结果的回调,那就是在里面(上面注释3的地方)处理我们的逻辑啦:if (result.getResultCode() == Activity.RESULT_OK) { Intent intent = result.getData(); ... }
使用 Activity Result API 的方式,我们再看下这两个问题:
- 业务启动多个
Activity,处理逻辑分开啦,互不干扰,你会发现都不用写在onActivityResult方法中啦; - 启动哪一个
Activity,也不需要维护requestCode啦
3、原理探究
既然是搞懂 Activity Result API,那我们继续往下挖一下。
(1)约束
首先先看一下 约束 。ActivityResultContracts 该类是 Android 提供的一些标准约束的集合,StartActivityForResult 只是其中的一个 static final class 类:
public static final class StartActivityForResult extends ActivityResultContract<Intent, ActivityResult> {
@Override
public Intent createIntent(Context context, Intent input) {
return input;
}
@Override
public ActivityResult parseResult(int resultCode, Intent intent) {
return new ActivityResult(resultCode, intent);
}
}
可以看到,这个 StartActivityForResult 类 继承 ActivityResultContract :该抽象类,约束了 启动 Activity 的 输入 及 输出
- I: 表示输入类型,在 约束使用
StartActivityForResult类时,启动器中的输入类型,为上述private ActivityResultLauncher<Intent> mActivityBLauncher中的Intent - O:表示输出类型,在 约束使用
StartActivityForResult类时,回调中的输出类型,为上述回调中的ActivityResult
public abstract class ActivityResultContract<I, O> {
public abstract Intent createIntent(Context context, I input);
public abstatc O parseResult(int resultCode, Intent intent);
}
通过上面的分析,我们可以总结到:这个 约束 决定了 输入类型 和 输出类型。
- 启动
Activity的时候,可以指定输入类型,并将这个输入在启动器的launch方法中传入进去,并通过createIntent方法来构造出Intent; - 获取
Activity的结果时,通过parseResult方法来获取指定地输出类型O。
(2)回调
接下来看一下回调:ActivityResultCallback , 当 Activity result 是可用时,回调此方法
public interface ActivityResultCallback<O> {
void onActivityResult(O result);
}
当使用 StartActivityForResult 约束时,输出类型 O 为 ActivityResult , 该类型表示 活动结果的容器:
public final class ActivityResult {
private final int mResultCode;
private final Intent mData;
public ActivityResult(int resultCode, Intent data) {
mResultCode = resultCode;
mData = data;
}
public Intent getData() {
return mData;
}
public int getResultCode() {
return mResultCode;
}
}
所以,当回调 onActivityResult 方法时:
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent intent = result.getData();
...
}
}
(3)注册
接下来,继续看下 registerForActivityResult 方法:
// ComponentActivity.java
public fianl <I, O> ActivityResultLauncher<I> registerForActivityResult(
ActivityResultContract<I, O> contract,
ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback); // 4
}
registerForActivityResult 方法,又转调用了另一个重载方法,注意到有一个新参数:mActivityResultRegistry
// ComponentActivity.java
private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {
@Override
public <I, O> void onLaunch(final int requestCode, ActivityResultContract<I, O> contract, I input,
ActivityOptionsCompat options) {
ComponentActivity activity = ComponentActivity.class;
// Start activity path
Intent intent = contract.createIntent(activity, input);
// startActivityForResult path
ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
}
}
我们可以看到,原来在 mActivityResultRegistry 中,封装了 startActivityForResult,那我们看下 ActivityResultRegistry 是何方神圣:
// ActivityResultRegistry.java
public abstract class ActivityResultRegistry {
public abstract <I, O> void onLaunch(int requestCode, ActivityResultContract<I, O> contract,
I input, ActivityOptionsCompat options);
public fianl <I, O> ActivityResultLauncher<I> register(final String key,
final LifecycleOwner lifecycleOwner,
final ActivityResultContract<I, O> contract,
final ActivityResultCallback<O> callback) {
... // lifecycle code
final int requestCode = registerKey(key); // 产生requestCode
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);
}
}
}
public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
doDispatch(key, resultCode, data, mKeyToCallback.get(key));
}
private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
@Nullable CallbackAndContract<O> callbackAndContract) {
callback.onActivityResult(contract.parseResult(resultCode, data));
}
private int registerKey(String key) {
Integer existing = mKeyToRc.get(key);
if (existing != null) {
return existing;
}
int rc = generateRandomNumber();
bindRcKey(rc, key);
return rc;
}
final void unregister(@NonNull String key) {
...
}
}
从这个 ActivityResultRegistry 我们可以看到 提供一个 抽象方法 onLaunch 、register 方法 、doDispatch方法
-
onLaunch方法是一个抽象方法,在ComponentActivty中实现该方法,封装startActivityForResult,返回一个mActivityResultRegistry参数,而该参数会传递到ComponentActivity类中的registerForActivityResult(contract, mActivityResultRegistry, callback);方法,即上述注释4的地方:// ComponentActivity.java public final <I, O> ActivityResultLauncher<I> registerForActivityResult(@NonNull ActivityResultContract<I, O> contract, ActivityResultRegistry registry, @NonNull ActivityResultCallback<O> callback) { return registry.register("activity_rq#" + this.mNextLocalRequestCode.getAndIncrement(), this, contract, callback); } -
在
register方法干了啥事呢?首先产生requestCode、然后 返回一个启动器ActivityResultLauncher<I>,那我们看下这个 启动器:// ActivityResultLauncher.java public abstract class ActivityResultLauncher<I> { public void launch(I input) { launch(input, null); } public abstract void launch(I input, ActivityOptionsCompat options); public abstract void unregister(); }这个启动器是一个抽象类,它是在
ActivityResultRegistry的register方法实现了这个抽象类,而在实现这个抽象类的时候,调用了ActivityResultRegistry自身的onLaunch抽象方法,而这个onLaunch抽象方法 在ComponentActivity中通过引入mActivityResultRegistry对象被实现了。该抽象类的launch方法,正是给外部调用的启动的地方,如:mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this)); -
doDispatch方法:在ComponentActivity中的onActivityResult拦截了 返回结果,并通过mActivityResultRegistry进行分发// ComponentActivity.java protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } }如上述的
ActivityResultRegistry.java中的doDispatch方法,对具体分发的内容 转出到 暴露外部的回调接口:ActivityResultCallback// ActivityResultRegistry.java private <O> void doDispatch(String key, int resultCode, @Nullable Intent data, @Nullable CallbackAndContract<O> callbackAndContract) { // 这里的contract.parseResult(resultCode, data) 就是我们外界定义的输出参数,也就是那个 约束中的输出类型 O callback.onActivityResult(contract.parseResult(resultCode, data)); }此时,这里的
callback就是我们在外部定义的 回调ActivityResultCallback
(4)启动
通过 registerForActivityResult 方法返回一个启动器,例如:mActivityBLauncher ,然后在我们的点击事件中启动这个 Activity :
mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this));
(5)资源回收
当页面要执行 onDestory 方法时,启动器还提供一个 unregister 方法用于释放资源,当然啦,你不手动调用也行,因为 ComponentActivity 已经引用了生命周期感知的 Lifecycle:
mActivityBLauncher.unregister();