activity result

452 阅读7分钟

使用

第一种使用方式在 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)

优缺点

  1. 第一种方式只能在 super.onStart() 调用之前调用,基本上限制了只能在 Activity 中使用,完全无法满足想在任何类中需要 startActivityForResult 的问题
  2. 第二种有两个缺点:
    • 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 内部会使用 mRcToKeymKeyToRc 两个 map 存储它们的映射关系,同时使用 mKeyToCallback 存储 key 与 callback 的映射关系。

当 activity 销毁时会调用 ActivityResultRegistry::onSaveInstanceState(),此时会将 mKeyToRc 存储至 bundle 中,在 ActivityResultRegistry::onRestoreInstanceState 中通过存储的 mKeyToRc 恢复销毁前的 mRcToKeymKeyToRc

因此销毁重建后 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>() {
        // ... 与第一种代码一样,忽略
    };
}