一、内存泄漏的常见场景与诊断方法
内存泄漏就像家里漏水的水龙头,虽然每次只漏一滴,但时间久了就会水漫金山。在Android开发中,最常见的场景就是Activity被静态对象持有引用。比如我们经常写的工具类:
// 技术栈:Android Java
public class ImageUtils {
private static Context sContext; // 静态变量持有Activity引用
public static void init(Context context) {
sContext = context; // 错误示范:静态变量持有Activity
}
public static void load(String url) {
// 使用sContext加载图片...
}
}
这段代码的问题在于:当Activity销毁时,由于ImageUtils类持有它的引用,导致GC无法回收该Activity。正确的做法应该是使用ApplicationContext:
public class ImageUtils {
private static Context sAppContext; // 使用Application上下文
public static void init(Context context) {
sAppContext = context.getApplicationContext(); // 正确做法
}
}
诊断内存泄漏可以使用Android Studio自带的Profiler工具。具体操作是:
- 打开Profiler -> Memory
- 执行可能泄漏的操作
- 触发GC多次
- 检查内存是否回落
二、四大内存优化实战技巧
2.1 使用弱引用处理回调
Handler是另一个常见的泄漏源:
// 危险代码示例
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 更新UI...
}
};
改进方案是使用静态Handler+弱引用:
// 优化后的Handler
private static class SafeHandler extends Handler {
private final WeakReference<Activity> mActivityRef;
public SafeHandler(Activity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = mActivityRef.get();
if (activity != null && !activity.isFinishing()) {
// 安全更新UI
}
}
}
2.2 优化图片加载
图片处理不当会直接导致OOM。推荐使用Glide时注意这些配置:
Glide.with(context)
.load(imageUrl)
.apply(new RequestOptions()
.override(targetWidth, targetHeight) // 按需加载
.format(DecodeFormat.PREFER_RGB_565) // 减少内存占用
.diskCacheStrategy(DiskCacheStrategy.ALL) // 启用缓存
)
.into(imageView);
2.3 对象池技术
对于频繁创建销毁的对象,可以使用对象池:
// 创建对象池
public class BitmapPool {
private static final int MAX_SIZE = 10;
private static Queue<Bitmap> pool = new LinkedList<>();
public static Bitmap getBitmap(int width, int height) {
Bitmap bitmap = pool.poll();
if (bitmap == null || bitmap.isRecycled()) {
return Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
}
return bitmap;
}
public static void recycle(Bitmap bitmap) {
if (pool.size() < MAX_SIZE) {
pool.offer(bitmap);
} else {
bitmap.recycle();
}
}
}
2.4 监控大对象分配
在Application中注册回调监控大内存分配:
// 监控大内存分配
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Debug.setAllocationLimit(1024 * 1024); // 1MB
Debug.setAllocationListener(new AllocationListener() {
@Override
public void onAllocation(int size) {
Log.w("Memory", "大内存分配:" + size + " bytes");
}
});
}
}
三、性能优化的进阶策略
3.1 启动速度优化
冷启动耗时主要消耗在:
- 加载主题样式
- 初始化第三方库
- 主线程耗时操作
优化方案示例:
// 延迟初始化非关键库
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 主线程只初始化必要组件
initEssentialComponents();
// 非关键库放到后台线程
new Handler().postDelayed(() -> {
initNonCriticalLibs();
}, 3000);
}
}
3.2 列表滑动优化
RecyclerView优化要点:
// RecyclerView优化配置
recyclerView.setHasFixedSize(true); // 固定尺寸提升性能
recyclerView.setItemViewCacheSize(20); // 增加缓存数量
recyclerView.setDrawingCacheEnabled(true); // 启用绘图缓存
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
// ViewHolder优化示例
public class OptimizedViewHolder extends RecyclerView.ViewHolder {
// 将findViewById结果缓存起来
private SparseArray<View> mCachedViews = new SparseArray<>();
public View getView(int id) {
View view = mCachedViews.get(id);
if (view == null) {
view = itemView.findViewById(id);
mCachedViews.put(id, view);
}
return view;
}
}
3.3 数据库查询优化
Room数据库的优化技巧:
// 使用@Transaction减少事务开销
@Dao
public interface UserDao {
@Transaction
@Query("SELECT * FROM users WHERE age > :minAge")
List<User> getUsersOlderThan(int minAge);
}
// 添加索引提升查询速度
@Database(entities = {User.class}, version = 1)
@TypeConverters({DateConverter.class})
public abstract class AppDatabase extends RoomDatabase {
@Override
public void init(@NonNull SupportSQLiteDatabase db) {
db.execSQL("CREATE INDEX idx_user_age ON users(age)");
}
}
四、工具链与最佳实践
4.1 使用LeakCanary自动检测
在build.gradle中添加:
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
LeakCanary会自动检测内存泄漏并生成报告,比手动检测效率高10倍以上。
4.2 使用StrictMode严苛模式
在Application中启用:
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
}
}
4.3 内存优化检查清单
上线前必做的10项检查:
- 所有Activity是否都有泄漏检测
- 大图是否都做了压缩处理
- 静态集合是否及时清理
- 广播是否正确注销
- 线程池是否合理管理
- 数据库游标是否关闭
- 文件流是否关闭
- 缓存大小是否合理
- 第三方库是否按需初始化
- 内存监控是否到位
五、总结与建议
内存优化是个持续的过程,建议建立以下机制:
- 新功能开发时进行内存影响评估
- Code Review时加入内存检查项
- 定期使用工具进行全量扫描
- 建立性能基线持续监控
记住优化原则:预防胜于治疗,监控优于修复。良好的编码习惯比事后优化更重要,比如:
- 及时释放不再使用的资源
- 避免在循环中创建对象
- 使用合适的数据结构
- 注意第三方库的内存开销
最后提醒,不要过度优化。优化前先测量,找到真正的瓶颈再动手。有时候用户感知不到的微秒级优化,可能带来代码可读性的显著下降,得不偿失。
评论