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

相关推荐
Kapaseker5 小时前
你不看会后悔的2025年终总结
android·kotlin
alexhilton8 小时前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
ji_shuke9 小时前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
sunnyday042611 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理12 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台12 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐12 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极12 小时前
Android Jetpack Compose折叠屏感知与适配
android
HelloBan13 小时前
setHintTextColor不生效
android
洞窝技术15 小时前
从0到30+:智能家居配网协议融合的实战与思考
android