求助:SplashActivity注册注销ConnectivityReceiver后仍内存泄漏
咱们先从可能的泄漏根源入手,一步步排查解决:
1. 先排查Receiver的引用持有问题
如果你的ConnectivityReceiver是非静态内部类,它会默认持有外部SplashActivity的强引用。哪怕你在onDestroy里注销了接收器,只要系统广播队列还持有这个Receiver的引用,就会把Activity拽着不让GC回收。
修复方案:
把ConnectivityReceiver改成静态内部类,如果需要调用Activity的方法,用WeakReference来弱引用Activity实例,避免强引用锁死:
public static class ConnectivityReceiver extends BroadcastReceiver { private WeakReference<SplashActivity> activityWeakRef; public ConnectivityReceiver(SplashActivity activity) { activityWeakRef = new WeakReference<>(activity); } @Override public void onReceive(Context context, Intent intent) { SplashActivity activity = activityWeakRef.get(); // 先判断Activity是否存活,再执行逻辑 if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) { // 处理网络状态变化逻辑 } } }
2. 确认广播注册/注销的上下文一致性
如果你用的是动态注册registerReceiver(receiver, intentFilter),一定要保证注册和注销用的是同一个上下文:比如注册时用SplashActivity.this,注销时也必须用这个实例,不能换成getApplicationContext(),否则系统会认为是两个不同的注册操作,导致原Receiver无法被正确注销。
另外,建议把注册/注销的时机调整到onResume/onPause,而不是onCreate/onDestroy——因为如果Activity被系统后台回收,onDestroy可能不会被执行,导致Receiver永远挂在系统里:
private ConnectivityReceiver connectivityReceiver; @Override protected void onResume() { super.onResume(); connectivityReceiver = new ConnectivityReceiver(this); IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(connectivityReceiver, filter); } @Override protected void onPause() { super.onPause(); if (connectivityReceiver != null) { unregisterReceiver(connectivityReceiver); connectivityReceiver = null; // 主动置空,切断引用 } }
3. 查看LeakCanary的引用链细节
LeakCanary的泄漏报告里会清晰展示引用链,你可以顺着链条找根源:比如是不是ConnectivityManager还持有Receiver的引用?或者有没有其他静态变量意外持有了Activity的实例?
举个例子,如果报告显示ConnectivityManager -> ConnectivityReceiver -> SplashActivity,那基本就是Receiver没被正确注销,或者Receiver持有Activity强引用导致的。
4. 更省心的替代方案:用Lifecycle组件管理广播
如果你的项目用了AndroidX,推荐用LifecycleObserver来自动管理广播的注册和注销,完全不用手动处理时机问题:
public class ConnectivityLifecycleObserver implements LifecycleObserver { private final Context appContext; private ConnectivityReceiver receiver; public ConnectivityLifecycleObserver(Context context) { this.appContext = context.getApplicationContext(); receiver = new ConnectivityReceiver(); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) void registerReceiver() { IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); appContext.registerReceiver(receiver, filter); } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) void unregisterReceiver() { appContext.unregisterReceiver(receiver); } }
然后在SplashActivity里添加观察者:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getLifecycle().addObserver(new ConnectivityLifecycleObserver(this)); }
这样Lifecycle会自动在Activity进入前台时注册广播,退到后台时注销,从根源避免泄漏问题。
内容的提问来源于stack exchange,提问作者nikhil n

