什么是内存泄漏?
在 Android 开发中,内存泄漏是指对象不再需要使用时,由于仍然被其他对象持有引用而无法被垃圾回收器(GC)回收的情况。这会导致应用内存使用量不断增加,最终可能引发 OutOfMemoryError
,导致应用崩溃或性能下降。
常见内存泄漏场景
1. 未释放的静态对象或单例
问题描述:静态对象或单例的生命周期与应用进程一致,如果它们持有了 Activity 或 Context 的引用,会导致这些短生命周期对象无法被回收。
示例代码:
public class MemoryLeakExample {
private static Context sContext;
public static void init(Context context) {
sContext = context; // 持有了Activity的引用
}
}
解决方案:
- 避免静态对象持有 Activity 的引用
- 使用 ApplicationContext 替代 Activity Context
- 使用弱引用(WeakReference)持有 Context
2. Handler 导致的内存泄漏
问题描述:Handler 默认持有其创建时所在 Looper 的引用,如果消息队列中有未处理的消息,Handler 会一直持有 Activity 的引用。
示例代码:
public class MemoryLeakActivity extends Activity {
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 延迟10秒执行
}
}, 10000);
}
}
关于 Handler 内存泄漏的深入分析:
- 在消息未处理时:Handler 持有 Activity 引用,导致泄漏
- 消息处理完成后:如果没有其他消息,Handler 不再持有引用,Activity 可被回收
- 但最佳实践是主动清理消息队列
解决方案:
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null); // 清除所有消息
}
或者使用静态 Handler 配合弱引用:
private static class MyHandler extends Handler {
private final WeakReference<Activity> mActivity;
public MyHandler(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = mActivity.get();
if (activity != null) {
// 处理消息
}
}
}
3. 非静态内部类导致的内存泄漏
问题描述:非静态内部类隐式持有外部类的引用,如果内部类执行长时间任务,会阻止外部类被回收。
示例代码:
public class MemoryLeakActivity extends Activity {
private class MyRunnable implements Runnable {
@Override
public void run() {
// 长时间任务
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new MyRunnable()).start();
}
}
解决方案:
- 将内部类改为静态类
- 使用弱引用持有外部类实例
private static class MyRunnable implements Runnable {
private final WeakReference<Activity> mActivity;
public MyRunnable(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void run() {
Activity activity = mActivity.get();
if (activity != null) {
// 执行任务
}
}
}
4. 长生命周期对象持有短生命周期对象的引用
问题描述:如 Application、Service 等长生命周期对象持有 Activity 的引用,导致 Activity 无法被回收。
示例代码:
public class MemoryLeakService extends Service {
private Activity mActivity; // 持有Activity引用
public void setActivity(Activity activity) {
mActivity = activity;
}
}
解决方案:
- 避免直接持有 Activity 引用
- 使用弱引用
- 使用回调接口替代直接引用
5. 未取消的 Observer 或 Listener
问题描述:注册的监听器(如 BroadcastReceiver、ContentObserver)在 Activity 销毁时未取消注册,导致内存泄漏。
示例代码:
public class MemoryLeakActivity extends Activity {
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 处理广播
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(receiver, new IntentFilter("SOME_ACTION"));
}
}
解决方案:
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver); // 取消注册
}
6. WebView 导致的内存泄漏
问题描述:WebView 是重量级组件,如果不正确释放,会持有 Activity 引用导致泄漏。(包括X5WebView)
示例代码:
public class MemoryLeakActivity extends Activity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = new WebView(this);
setContentView(webView);
}
}
解决方案:
@Override
protected void onDestroy() {
super.onDestroy();
if (webView != null) {
webView.destroy();
webView = null;
}
}
总结
Android 内存泄漏通常是由于生命周期较长的对象持有了短生命周期对象的引用。要避免内存泄漏,开发者应当:
- 谨慎使用静态变量和单例模式
- 及时清理 Handler 的消息队列
- 将可能持有外部类引用的内部类改为静态类
- 避免长生命周期对象直接持有短生命周期对象
- 及时取消注册各种监听器和观察者
- 正确释放重量级组件如 WebView
通过合理使用弱引用、及时释放资源、遵循组件生命周期管理,可以有效预防内存泄漏问题,提升应用性能和稳定性。