BroadcastReceiver监听网络变化时,第二、三Activity接口引用为空求助
问题分析与解决方案
我来帮你梳理下问题的核心原因,然后给出可行的解决办法:
为什么第二、第三个Activity的接口引用会为空?
你现在的代码存在两个关键问题:
- 每个Activity都创建了独立的
NetworkChangeReceiver实例:第一个Activity的Receiver被registerReceiver注册到了系统,所以系统会持有它的强引用,不会被GC回收;但第二、第三个Activity的Receiver只是onCreate里的局部变量,既没有注册到系统,onCreate执行完后也没有其他强引用持有它,很快就会被垃圾回收,自然接口引用就变成空了。 - 第二、第三个Activity没有注册广播接收器:就算你把这两个Activity的Receiver注册到系统,那也是三个完全独立的Receiver实例,网络变化时三个都会被触发,但这不是你想要的“一个Receiver通知所有存活Activity”的效果。
另外还有隐藏风险:你的Receiver持有Activity的强引用,如果Activity销毁时没有注销Receiver,会导致Activity无法被回收,造成内存泄漏。
解决方案一:修复广播接收器方案(兼容旧版本)
我们需要改成单例Receiver+弱引用集合的模式,确保全局只有一个Receiver被系统监听,同时避免内存泄漏,并且只通知当前存活的Activity:
1. 修改NetworkChangeReceiver为单例+弱引用
class NetworkChangeReceiver : BroadcastReceiver() { // 用弱引用集合保存接口实例,避免持有Activity强引用导致内存泄漏 private val snackbarInterfaces = mutableSetOf<WeakReference<InternetSnackbarInterface>>() // 单例实例,全局唯一 companion object { val instance: NetworkChangeReceiver by lazy { NetworkChangeReceiver() } } // 注册接口 fun registerInterface(snackbarInterface: InternetSnackbarInterface) { snackbarInterfaces.add(WeakReference(snackbarInterface)) // 自动清理已经被回收的无效引用 snackbarInterfaces.removeIf { it.get() == null } } // 注销接口 fun unregisterInterface(snackbarInterface: InternetSnackbarInterface) { snackbarInterfaces.removeIf { it.get() == snackbarInterface } } override fun onReceive(context: Context, intent: Intent?) { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val activeNetworkInfo = connectivityManager.activeNetworkInfo val isConnected = activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting // 先清理失效引用,避免空指针 snackbarInterfaces.removeIf { it.get() == null } if (isConnected){ snackbarInterfaces.forEach { it.get()?.hideNoInternetSnackbar() } }else{ snackbarInterfaces.forEach { it.get()?.showNoInternetSnackbar("No internet connection") } } } interface InternetSnackbarInterface { fun showNoInternetSnackbar(message: String) fun hideNoInternetSnackbar() } }
2. 在Application中全局注册Receiver
这样只需要注册一次,避免每个Activity重复注册:
class MyApp : Application() { override fun onCreate() { super.onCreate() val intentFilter = IntentFilter() intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE") // 注册单例Receiver registerReceiver(NetworkChangeReceiver.instance, intentFilter) } }
记得在AndroidManifest.xml中配置这个Application:
<application android:name=".MyApp" ...> ... </application>
3. 修改所有Activity的代码
统一在onResume注册接口,onPause注销接口(确保只通知当前可见的Activity):
class FirstActivity: AppCompatActivity(), NetworkChangeReceiver.InternetSnackbarInterface { private lateinit var networkReceiver: NetworkChangeReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) networkReceiver = NetworkChangeReceiver.instance } override fun onResume() { super.onResume() // Activity可见时注册接口 networkReceiver.registerInterface(this) } override fun onPause() { super.onPause() // Activity不可见时注销接口,避免通知已不可见的页面 networkReceiver.unregisterInterface(this) } var internetSnackbar : Snackbar? =null private fun showSnackbar(message : String,length : Int){ val parentLayout = findViewById<View>(android.R.id.content) internetSnackbar = Snackbar.make(parentLayout, message, length) internetSnackbar?.show() } override fun hideNoInternetSnackbar() { internetSnackbar?.dismiss() } override fun showNoInternetSnackbar(message: String) { showSnackbar(message, Snackbar.LENGTH_INDEFINITE) } }
第二、第三个Activity完全复用这个代码结构即可,不需要再区分“第一/第二/第三”接口。
解决方案二:使用官方推荐的NetworkCallback(更可靠)
从Android 7.0(API 24)开始,CONNECTIVITY_CHANGE广播在后台应用中会被限制接收,官方更推荐使用ConnectivityManager.NetworkCallback来监听网络变化,这个方案更稳定,也更符合现代Android开发规范:
1. 创建单例网络监听类
class NetworkMonitor private constructor(private val context: Context) { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager private val snackbarInterfaces = mutableSetOf<WeakReference<NetworkSnackbarInterface>>() // 网络变化回调 private val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { super.onAvailable(network) notifyConnectionChange(true) } override fun onLost(network: Network) { super.onLost(network) notifyConnectionChange(false) } override fun onUnavailable() { super.onUnavailable() notifyConnectionChange(false) } } // 通知所有注册的Activity网络变化 private fun notifyConnectionChange(isConnected: Boolean) { snackbarInterfaces.removeIf { it.get() == null } if (isConnected) { snackbarInterfaces.forEach { it.get()?.hideNoInternetSnackbar() } } else { snackbarInterfaces.forEach { it.get()?.showNoInternetSnackbar("No internet connection") } } } fun registerInterface(interface: NetworkSnackbarInterface) { snackbarInterfaces.add(WeakReference(interface)) snackbarInterfaces.removeIf { it.get() == null } } fun unregisterInterface(interface: NetworkSnackbarInterface) { snackbarInterfaces.removeIf { it.get() == interface } } // 开始监听网络 fun startMonitoring() { val request = NetworkRequest.Builder().build() connectivityManager.registerNetworkCallback(request, networkCallback) } // 停止监听网络(可选,一般在Application销毁时调用) fun stopMonitoring() { connectivityManager.unregisterNetworkCallback(networkCallback) } interface NetworkSnackbarInterface { fun showNoInternetSnackbar(message: String) fun hideNoInternetSnackbar() } companion object { // 获取单例实例 fun getInstance(context: Context): NetworkMonitor { return NetworkMonitor(context.applicationContext) } } }
2. 在Application中启动监听
class MyApp : Application() { private lateinit var networkMonitor: NetworkMonitor override fun onCreate() { super.onCreate() networkMonitor = NetworkMonitor.getInstance(this) networkMonitor.startMonitoring() } }
3. 修改Activity代码
和广播方案类似,在onResume/onPause中注册/注销接口:
class FirstActivity: AppCompatActivity(), NetworkMonitor.NetworkSnackbarInterface { private lateinit var networkMonitor: NetworkMonitor override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) networkMonitor = NetworkMonitor.getInstance(this) } override fun onResume() { super.onResume() networkMonitor.registerInterface(this) } override fun onPause() { super.onPause() networkMonitor.unregisterInterface(this) } var internetSnackbar : Snackbar? =null private fun showSnackbar(message : String,length : Int){ val parentLayout = findViewById<View>(android.R.id.content) internetSnackbar = Snackbar.make(parentLayout, message, length) internetSnackbar?.show() } override fun hideNoInternetSnackbar() { internetSnackbar?.dismiss() } override fun showNoInternetSnackbar(message: String) { showSnackbar(message, Snackbar.LENGTH_INDEFINITE) } }
内容的提问来源于stack exchange,提问作者Abdelrahman Rizq
相关产品推荐
相关产品推荐

