如何注册回调函数通知所有Fragment操作完成?Web服务登录场景适配
嘿,这个场景我太熟了!之前做项目的时候也遇到过类似的问题——Fragment加载数据时登录还没完成,得等登录结束再触发请求。给你两个实用的方案,都是Android开发里常用的解决思路:
方案一:自定义回调接口 + 单例类管理回调列表
这是最直接的原生实现方式,完全贴合你当前的单例认证类场景:
1. 定义登录状态回调接口
先写一个接口,用来规范登录完成后的回调行为:
// 如果用Java,换成Java接口语法即可 interface LoginStatusCallback { fun onLoginCompleted(isSuccess: Boolean, message: String?) }
2. 在认证单例类里维护回调列表
把回调集合放在单例中,同时提供注册、注销和触发回调的方法(注意用线程安全的集合避免并发问题):
object AuthSingleton { // 用CopyOnWriteArrayList防止并发修改异常 private val callbacks = CopyOnWriteArrayList<LoginStatusCallback>() // 注册回调 fun registerCallback(callback: LoginStatusCallback) { if (!callbacks.contains(callback)) { callbacks.add(callback) } } // 注销回调!务必在Fragment销毁时调用,避免内存泄漏 fun unregisterCallback(callback: LoginStatusCallback) { callbacks.remove(callback) } // 登录完成时通知所有注册的回调 private fun notifyLoginCompleted(isSuccess: Boolean, message: String?) { callbacks.forEach { it.onLoginCompleted(isSuccess, message) } } // 你的原有登录方法,登录结束后触发通知 fun login(username: String, password: String) { // 模拟网络登录请求 ApiClient.login(username, password) { success, msg -> // 处理登录结果后,通知所有Fragment notifyLoginCompleted(success, msg) } } // 新增:检查当前是否已登录的方法 fun isLoggedIn(): Boolean { // 这里写你判断登录状态的逻辑,比如读取本地token return false } }
3. 在Fragment中注册/注销并实现回调
Fragment要在合适的生命周期节点注册和注销回调,比如视图创建时注册,视图销毁时注销:
class MyFragment : Fragment(), LoginStatusCallback { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 注册回调 AuthSingleton.registerCallback(this) // 先检查当前是否已经登录,如果已登录直接加载数据 if (AuthSingleton.isLoggedIn()) { loadDataFromWeb() } } override fun onDestroyView() { super.onDestroyView() // 必须注销回调!否则Fragment会被单例持有,无法被GC回收 AuthSingleton.unregisterCallback(this) } // 实现回调方法,登录完成后触发数据加载 override fun onLoginCompleted(isSuccess: Boolean, message: String?) { if (isSuccess) { loadDataFromWeb() } else { // 处理登录失败的情况,比如显示错误提示 Toast.makeText(requireContext(), message ?: "登录失败", Toast.LENGTH_SHORT).show() } } private fun loadDataFromWeb() { // 这里写你的网络请求加载数据逻辑 } }
方案二:用LiveData实现(更Android化的方案)
如果你项目里用Jetpack组件,推荐用LiveData——它自带生命周期感知能力,不用手动注销回调,能自动避免内存泄漏:
1. 在单例类里定义LiveData
object AuthSingleton { // 内部用MutableLiveData修改状态,对外暴露不可变的LiveData private val _loginStatus = MutableLiveData<LoginResult?>() val loginStatus: LiveData<LoginResult?> = _loginStatus // 封装登录结果的数据类 data class LoginResult(val isSuccess: Boolean, val message: String?) fun login(username: String, password: String) { ApiClient.login(username, password) { success, msg -> // 用postValue在子线程更新LiveData _loginStatus.postValue(LoginResult(success, msg)) } } fun isLoggedIn(): Boolean { return _loginStatus.value?.isSuccess ?: false } }
2. 在Fragment中观察LiveData
class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 观察登录状态变化,用viewLifecycleOwner绑定Fragment视图生命周期 AuthSingleton.loginStatus.observe(viewLifecycleOwner) { result -> result?.let { if (it.isSuccess) { loadDataFromWeb() } else { Toast.makeText(requireContext(), it.message ?: "登录失败", Toast.LENGTH_SHORT).show() } } } // 检查当前登录状态,已登录则直接加载 if (AuthSingleton.isLoggedIn()) { loadDataFromWeb() } } private fun loadDataFromWeb() { // 加载数据逻辑 } }
关键注意事项
- 方案一必须手动注销回调,否则Fragment会被单例持有,引发内存泄漏。
- 方案二一定要用
viewLifecycleOwner观察LiveData,这样Fragment视图销毁时会自动移除观察者,无需手动处理。 - 不管用哪种方案,只要MainActivity触发登录,所有注册了回调/观察了LiveData的Fragment都会收到登录完成的通知,进而执行数据加载逻辑。
内容的提问来源于stack exchange,提问作者Augusto
相关产品推荐
相关产品推荐

