本文讲解Android 开发中常见内存泄漏场景及其解决方案,内容包括代码示例、原因分析以及最佳实践建议。
1. 静态变量导致的内存泄漏
静态变量的生命周期与应用进程一致,如果静态变量持有了对 Activity 或其他大对象的引用,就可能导致内存泄漏。
场景示例
java
public class MemoryLeakExample {
// 静态变量持有 Activity 的引用
private static Context sContext;
public static void setContext(Context context) {
sContext = context;
}
}
如果在 onCreate()
方法中调用了 MemoryLeakExample.setContext(this)
,即使 Activity 销毁,sContext
仍然持有对 Activity 的引用,导致内存泄漏。
解决方案
- 避免使用静态变量持有对 Context 的引用。
- 使用 ApplicationContext 替代 Activity 的 Context。
修复代码
java
public class MemoryLeakExample {
private static Context sContext;
public static void setContext(Context context) {
// 使用 ApplicationContext 避免泄漏
sContext = context.getApplicationContext();
}
}
2. Handler 导致的内存泄漏
Handler
会隐式持有外部类的引用,导致外部类无法被垃圾回收。
场景示例
java
public class MainActivity extends AppCompatActivity {
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// 处理消息
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.postDelayed(() -> {
// 延迟任务
}, 10000);
}
}
如果在任务执行前 Activity 被销毁,Handler
仍然持有对 Activity 的引用。
解决方案
- 将 Handler 定义为静态内部类,避免隐式引用外部类。
- 使用弱引用(WeakReference)来引用外部类。
修复代码
java
public class MainActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
private final WeakReference activityReference;
public MyHandler(MainActivity activity) {
super(Looper.getMainLooper());
activityReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
MainActivity activity = activityReference.get();
if (activity != null) {
// 处理消息
}
}
}
private final MyHandler handler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.postDelayed(() -> {
// 延迟任务
}, 10000);
}
}
3. 非静态内部类持有外部类的引用
非静态内部类会隐式持有其外部类的引用,如果长时间持有,则可能导致内存泄漏。
场景示例
java
public class MainActivity extends AppCompatActivity {
private class MyTask extends AsyncTask {
@Override
protected Void doInBackground(Void... voids) {
// 执行异步任务
return null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MyTask().execute();
}
}
如果 MyTask
执行时间较长,而 Activity 已销毁,则会导致泄漏。
解决方案
- 将内部类声明为静态。
- 使用弱引用(WeakReference)访问外部类实例。
修复代码
java
public class MainActivity extends AppCompatActivity {
private static class MyTask extends AsyncTask {
private final WeakReference activityReference;
MyTask(MainActivity activity) {
activityReference = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(Void... voids) {
// 执行异步任务
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
MainActivity activity = activityReference.get();
if (activity != null) {
// 更新 UI
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MyTask(this).execute();
}
}
4. 监听器或回调未正确移除
监听器或回调注册后,如果不及时移除,会导致对象无法释放。
场景示例
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = findViewById(R.id.my_view);
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
如果未移除 OnAttachStateChangeListener
,MainActivity
的引用可能被保留。
解决方案
在适当的生命周期方法中移除监听器或回调。
修复代码
java
public class MainActivity extends AppCompatActivity {
private View.OnAttachStateChangeListener listener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = findViewById(R.id.my_view);
listener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
view.addOnAttachStateChangeListener(listener);
}
@Override
protected void onDestroy() {
super.onDestroy();
View view = findViewById(R.id.my_view);
if (view != null && listener != null) {
view.removeOnAttachStateChangeListener(listener);
}
}
}
5. 单例模式导致的内存泄漏
单例对象的生命周期与应用一致,如果单例持有了对 Context
或 Activity 的引用,就会导致泄漏。
场景示例
java
public class Singleton {
private static Singleton instance;
private Context context;
private Singleton(Context context) {
this.context = context;
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
在获取 Singleton
时传入了 Activity
的 Context
,会导致泄漏。
解决方案
- 使用 ApplicationContext。
- 避免单例直接持有 Context。
修复代码
java
public class Singleton {
private static Singleton instance;
private Context context;
private Singleton(Context context) {
// 使用 ApplicationContext 避免泄漏
this.context = context.getApplicationContext();
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
6. 其他常见场景
6.1 Bitmap 未及时回收
java
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image);
// 使用完毕后应回收
bitmap.recycle();
6.2 WebView 泄漏
java
WebView webView = new WebView(context);
webView.destroy();
以上示例涵盖了 Android 中常见的内存泄漏场景及其解决方法,通过合理使用静态类、弱引用以及生命周期管理,可以有效减少内存泄漏问题。