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