动态注册广播内存泄漏预防

130 阅读2分钟

在 Android 开发中,动态注册广播(通过 registerReceiver())若处理不当容易引发内存泄漏。以下是预防内存泄漏的核心方案和代码示例:


1. 生命周期成对管理

确保在组件的生命周期中成对调用 registerReceiver()unregisterReceiver()

示例:在 Activity 中注册/注销

public class MainActivity extends AppCompatActivity {
    private BroadcastReceiver mReceiver;

    @Override
    protected void onStart() {
        super.onStart();
        // 动态注册广播
        mReceiver = new MyBroadcastReceiver();
        IntentFilter filter = new IntentFilter("CUSTOM_ACTION");
        registerReceiver(mReceiver, filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 确保注销广播接收器
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
            mReceiver = null;
        }
    }
}
  • 关键点
    • onStart() 注册,在 onStop() 注销(或 onResume()/onPause() 成对使用)。
    • 避免在 onDestroy() 中注销,因为某些情况下可能无法保证执行(如系统回收资源时)。

2. 避免内部类隐式引用

匿名内部类或非静态内部类会隐式持有外部类(如 Activity)的引用,导致无法释放。

改进方案:使用静态内部类 + 弱引用

public class MainActivity extends AppCompatActivity {
    private MyBroadcastReceiver mReceiver;

    // 静态内部类,不持有外部类的隐式引用
    private static class MyBroadcastReceiver extends BroadcastReceiver {
        private WeakReference<MainActivity> mActivityRef;

        MyBroadcastReceiver(MainActivity activity) {
            mActivityRef = new WeakReference<>(activity);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            MainActivity activity = mActivityRef.get();
            if (activity != null) {
                // 处理广播逻辑
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mReceiver = new MyBroadcastReceiver(this);
        IntentFilter filter = new IntentFilter("CUSTOM_ACTION");
        registerReceiver(mReceiver, filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
            mReceiver = null;
        }
    }
}

3. 避免在 Application 或长期存活组件中注册

Application 或长期存活的组件(如单例)中注册广播时,需手动管理生命周期,否则接收器会一直持有引用。

错误示例

public class MyApp extends Application {
    private BroadcastReceiver mReceiver;

    @Override
    public void onCreate() {
        super.onCreate();
        // 错误:在 Application 中注册但未注销
        mReceiver = new MyReceiver();
        registerReceiver(mReceiver, new IntentFilter("CUSTOM_ACTION"));
    }
}

改进方案

在合适的时机(如组件销毁时)主动调用 unregisterReceiver()


4. 使用 LocalBroadcastManager(已过时,替代方案)

LocalBroadcastManager 已弃用,但其设计思想值得借鉴:使用应用内局域广播减少泄漏风险。替代方案为:

  • 使用 LiveData 或事件总线(如 EventBusRxJava
  • 直接使用 Context 注册,但严格控制生命周期。

5. 检测工具辅助

使用工具检测内存泄漏:

  • LeakCanary:自动检测内存泄漏并生成报告。
  • Android Profiler:手动分析内存使用情况。

总结

关键点解决方案
生命周期管理onStart()/onStop()onResume()/onPause() 中成对注册/注销。
避免隐式引用使用静态内部类 + 弱引用持有外部类。
避免长期持有不在 Application 或单例中随意注册广播。
替代方案使用 LiveData 或事件总线替代部分广播场景。

通过以上措施,可有效预防动态注册广播导致的内存泄漏问题。