使用
第一种使用方式在 activity 中
private val mR : ActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
}
mR.launch(intent)
第二种在非 activity 中使用
val i = Intent(mainActivity, SecondActivity::class.java)
val getContent =
// 通过 activity 实例拿到 activityResultRegistry 对象,再调用 register() 方法
// 会返回 ActivityResultLauncher 对象
mainActivity.activityResultRegistry.register(
"templ" + System.currentTimeMillis(),
ActivityResultContracts.StartActivityForResult()
) { uri: ActivityResult? ->
}
getContent.launch(i)
优缺点
- 第一种方式
只能在 super.onStart() 调用之前调用,基本上限制了只能在 Activity 中使用,完全无法满足想在任何类中需要 startActivityForResult 的问题 - 第二种有两个缺点:
callback 存在内存泄露:通过下面源码发现 callback 会保存中 ActivityResultRegistry 中,所以对于一次性的 startActivityForResult(比如点击事件中)就会存在内存泄露问题 —— 拿到结果后这个 callback 应该被立即回收掉的,但由于 Registry 中存在引用,导致无法被回收。这个问题好解决,只需要在 callback 中手动 unregister 掉即可
public class AutoUnregisterResultCallback<O> implements ActivityResultCallback<O> { private ActivityResultLauncher<?> launcher; // 把 ActivityResultLauncher 保存起来,用于在回调时 unregister 掉 public <P> void setLauncher(ActivityResultLauncher<P> launcher) { this.launcher = launcher; } @Override public final void onActivityResult(O o) { if (launcher != null) { // 通过 ActivityResultLauncher::launcher() 启动 activity // 在 launcher 中定义有 unregister() 方法 launcher.unregister(); } onActivityResult(o, false); } protected void onActivityResult(O o, boolean unused) { } }activity 销毁重建时无法拿到返回结果。解决方法见下
activity 销毁重建导致的问题
使用第二种方式在销毁重建时拿不到返回结果,但第一种可以拿到(有个坑,后面讲)。先分析下为什么第一种没问题
使用 result api 时有两个关键值:key 与 requestCode。key 是通过第二种方式启动时传入的 key(第一种方式内部会自动生成一个 key),requestCode 是 startActivityForResult 中的 requestCode。ActivityResultRegistry 内部会使用 mRcToKey 与 mKeyToRc 两个 map 存储它们的映射关系,同时使用 mKeyToCallback 存储 key 与 callback 的映射关系。
当 activity 销毁时会调用 ActivityResultRegistry::onSaveInstanceState(),此时会将 mKeyToRc 存储至 bundle 中,在 ActivityResultRegistry::onRestoreInstanceState 中通过存储的 mKeyToRc 恢复销毁前的 mRcToKey 与 mKeyToRc。
因此销毁重建后 setResult() 的结果是可以正确返回至 ActivityResultRegistry 中的:通过 onActivityResult() 拿到 requestCode,由 mRcToKey 拿到 key,由 key 拿到 callback,最终正确返回至调用处。逻辑可参考下面的返回相关的源码分析部分。
这里有一个问题:key 与 callback 对应。在 onSaveInstanceState() 中只存储了 key 没有存储 callback,一旦 activity 销毁 ActivityResultRegistry 也会随之销毁,内部存储的 callback 也会被销毁,那在重建后怎么通过 key 拿到 callback 呢?
原因是:当 activity 重建时,会重新执行 activity 的生命周期(当然也会初始化它的全局变量),如果在 重建过程调用了 registerForActivityResult() 就会将新生成的 callback 与 key 绑定,从而能从 key 拿到 callback,只不过此时的 callback 是新的,而不是销毁前旧的。这里有个关键点:一定要保存新旧 callback 对应的 key 相同,这就是第一种方式存在的坑。
// 在 activity 中定义全局变量,将 activity 重建时会重新为 launcher 赋值
// 同时 registerForActivityResult 内部也会将 callback 与 key 绑定
val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
}
假设使用在 onClick 中使用了第二种方式,在重建过程中并不会重新执行 onClick 中的代码,不会调用 ActivityResultRegistry::registry(),也就无法将销毁的 callback 恢复。所以在 onActivityResult() 过程中没法通过 key 拿到 callback —— 因为 key 没有对应的 callback
解决办法:在 activity 重建时再调用一次 ActivityResultRegistry::registry(),注意要保证两个 key 是一样:
- 通过 lifecycle.addObserver()
解决不了这个问题。因为新 activity 重建时这个 observer 拿不到回调 通过 application.registerActivityLifecycleCallbacks(),注意处理 lifecycleCallbacks 的内存泄露问题application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { // 恢复时,取出记录的 key 再重新 register 一次 if (savedInstanceState?.containsKey("launcher_key") == true) { val savedKey = savedInstanceState.getString("launcher_key")!! (activity as FragmentActivity).activityResultRegistry .register( savedKey, ActivityResultContracts.StartActivityForResult(), callback ) // !!!!要将该 lifecycleCallbacks 取消掉,否则 lifecycleCallbacks 会内存泄露 activity.application.unregisterActivityLifecycleCallbacks(this) } } override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { // 将本次 register 的 key 记录下来 outState.putString("launcher_key", key) } })
registerForActivityResult 导致的顺序问题
分析上面的问题时说过 registerForActivityResult() 内部会自动生成 key:该 key 是由一个递增序列+固定字符串组成,相同的 registerForActivityResult() 调用顺序会保证新旧 callback 对应同一个 key,一旦调用顺序不一致就会出现 callback 回调错乱问题。比如下面代码
if (savedInstanceState?.containsKey("test") == true) {
// 此处只有销毁重建时才执行
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// callback 2
}
}
// 正常新建、销毁新建都会执行
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), callback)
比如上面代码销毁重建时先注册 callback2 后注册的 callback,那么 callback2 就会占用重建前 callback 对应的 key,导致使用 launcher 获取返回结果时最终会回调至 callback2 中。
解决办法:不使用 registerForActivityResult 方法,自己调用 register
// 跟第二种使用方法一样,只不过多传了一个参数 LifecycleOwner,用于处理生命周期
launcher =
activityResultRegistry.register(
"testkey",
this, // LifecycleOwner,这是跟第一种最大的区别
ActivityResultContracts.StartActivityForResult(),
callback
)
源码
请求
整体逻辑非常简单,以一种伪代码表示如下。只不过 result api 处理了生命周期等各种条件,所以代码比较多。
private val mCallbacks = HashMap<Int, Any>()
fun launch(callback: Any, intent: Intent) {
// 随机生成 requestCode
val rc = randomRC()
mCallbacks[rc] = callback
startActivityForResult(intent, rc)
}
以第一种方式作为切入点。Activity::registerForActivityResult() 最终会调用 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();
if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
// 如果已处于 STATED 及以上状态就会抛异常,就这是使用 result api 时为什么会说有生命周期限制
throw new IllegalStateException(xxxx);
}
// 随机生成 requestCode
// 同时使用 mKeyToRc 与 mRcToKey 分别存储 key:requestCode 与 requestCode:key 的映射关系
// 也就是说不是没有 requestCode,而是 result api 内部进行了封装
registerKey(key);
// 处理生命周期
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
LifecycleEventObserver observer = new LifecycleEventObserver() {
// ...
};
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);
// 返回一个 ActivityResultLauncher 对象,外界会使用它的 launch 方法
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
// 根据 key 拿到 requestCode
Integer innerCode = mKeyToRc.get(key);
mLaunchedKeys.add(key);
try {
// onLaunch 是 ActivityResultRegistry 中的方法
// 它的具体实现在 Activity 中
// 具体内部其实就是调用 startActivityForResult
onLaunch(innerCode, contract, input, options);
} catch (Exception e) {
mLaunchedKeys.remove(key);
throw e;
}
}
// ....
};
}
上述代码的整体逻辑很清晰,现在看一下关于生命周期的处理
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
@SuppressWarnings("deprecation")
public void onStateChanged(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_START.equals(event)) {
// 只有在 ON_START 状态时才将结果传给接收者
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
// 如果有解析过的结果
if (mParsedPendingResults.containsKey(key)) {
@SuppressWarnings("unchecked")
final O parsedPendingResult = (O) mParsedPendingResults.get(key);
mParsedPendingResults.remove(key);
// 直接将结果传给接收者,也就是使用时定义的 lambda 表达式
callback.onActivityResult(parsedPendingResult);
}
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
if (pendingResult != null) {
mPendingResults.remove(key);
// 如果有未处理的,就调用 contract.parseResult 先处理
// 再将结果传递给接收者
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);
}
}
};
返回
返回结果还是在 onActivityResult() 中,该方法用于接收另一个 Activity 的返回结果不可能被抛弃
// 由 onActivityResult() 调起
public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
// 根据 rc 拿到 key
String key = mRcToKey.get(requestCode);
// 根据 key 拿到 CallbackAndContract,它包含有 callback 以及 contract
doDispatch(key, resultCode, data, mKeyToCallback.get(key));
return true;
}
private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
@Nullable CallbackAndContract<O> callbackAndContract) {
if (callbackAndContract != null && callbackAndContract.mCallback != null
&& mLaunchedKeys.contains(key)) {
ActivityResultCallback<O> callback = callbackAndContract.mCallback;
ActivityResultContract<?, O> contract = callbackAndContract.mContract;
// 调用 onActivityResult() 将最终处理好的结果返回给调用者
callback.onActivityResult(contract.parseResult(resultCode, data));
mLaunchedKeys.remove(key);
} else {
// 如果当前无法处理,就将结果暂存
// Remove any parsed pending result
mParsedPendingResults.remove(key);
// And add these pending results in their place
mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
}
}
第二种使用方法
它逻辑与第一种基本一样,只不过不再判断生命周期
public final <I, O> ActivityResultLauncher<I> register(
@NonNull final String key,
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
// 同上
registerKey(key);
// 没有任何生命周期判断,直接存储等待返回结果
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()));
}
return new ActivityResultLauncher<I>() {
// ... 与第一种代码一样,忽略
};
}