You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BroadcastReceiver监听网络变化时,第二、三Activity接口引用为空求助

问题分析与解决方案

我来帮你梳理下问题的核心原因,然后给出可行的解决办法:

为什么第二、第三个Activity的接口引用会为空?

你现在的代码存在两个关键问题:

  1. 每个Activity都创建了独立的NetworkChangeReceiver实例:第一个Activity的Receiver被registerReceiver注册到了系统,所以系统会持有它的强引用,不会被GC回收;但第二、第三个Activity的Receiver只是onCreate里的局部变量,既没有注册到系统,onCreate执行完后也没有其他强引用持有它,很快就会被垃圾回收,自然接口引用就变成空了。
  2. 第二、第三个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:10:23