Android中常见内存泄漏的场景和解决方案

本文讲解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) {
            }
        });
    }
}

如果未移除 OnAttachStateChangeListenerMainActivity 的引用可能被保留。

解决方案

在适当的生命周期方法中移除监听器或回调。

修复代码

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 时传入了 ActivityContext,会导致泄漏。

解决方案

  • 使用 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 中常见的内存泄漏场景及其解决方法,通过合理使用静态类、弱引用以及生命周期管理,可以有效减少内存泄漏问题。

相关推荐
阿巴斯甜7 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker8 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95279 小时前
Andorid Google 登录接入文档
android
黄林晴10 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android