0x0.必现步骤:打开app,退出app.内存泄漏检测工具LeakCanary必报.
0x1.LeakCanary就一定准吗?不见得.秉着尽信它不如不用它原则.启用adb校验.如图所示,Activities数量一直恒定,那内存泄漏真心没跑了.
输入 adb shell dumpsys meminfo -a 包名
图片证据
0x2.分析内存hprof文件com.blankj.utilcode.util.UtilsActivityLifecycleImpl.mActivityList持有activity无法释放
内存泄漏0x3.debug断点UtilsActivityLifecycleImpl.mActivityList 发现有两处.1是在生命周期onActivityCreated会被赋值2是getTopActivity时有机会会被赋值.为什么叫有机会呢?当app关闭时,生命周期正常分发mActivityList会被清空,此时调用getTopActivity就会被赋值,从而引起mActivityList持有activity无法被释放
image.png0x4.结论.Activity activity = ActivityUtils.getTopActivity();//请注意该函数调用时机!
image.png0x5.如上图所示.我只想获取栈顶activity.不影响mActivityList变动!把源码copy出来即可
package com.sinochem.helper;
import android.app.Activity;
import android.util.Log;
import com.blankj.utilcode.util.ActivityUtils;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @className: LeakHelper
* @description:LeakHelper.getTopActivity();
* @author:
* @date: 2023/1/16 9:05 上午 星期一
**/
public class LeakHelper {
public static void fixInputMethodManagerLeakCompat() {
ActivityUtils.startActivity(DumpActivity.class);
}
public static Activity getTopActivity() {
List<Activity> activityList = getActivityList();
for (Activity activity : activityList) {
if (!ActivityUtils.isActivityAlive(activity)) {
continue;
}
return activity;
}
return null;
}
private static List<Activity> getActivityList() {
List<Activity> reflectActivities = getActivitiesByReflect();
return reflectActivities;
}
private static Object getActivityThread() {
Object activityThread = getActivityThreadInActivityThreadStaticField();
if (activityThread != null) {
return activityThread;
}
return getActivityThreadInActivityThreadStaticMethod();
}
private static Object getActivityThreadInActivityThreadStaticMethod() {
try {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
return activityThreadClass.getMethod("currentActivityThread").invoke(null);
} catch (Exception e) {
Log.e("UtilsActivityLifecycle", "getActivityThreadInActivityThreadStaticMethod: " + e.getMessage());
return null;
}
}
private static Object getActivityThreadInActivityThreadStaticField() {
try {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
return sCurrentActivityThreadField.get(null);
} catch (Exception e) {
Log.e("UtilsActivityLifecycle", "getActivityThreadInActivityThreadStaticField: " + e.getMessage());
return null;
}
}
public static List<Activity> getActivitiesByReflect() {
LinkedList<Activity> list = new LinkedList<>();
Activity topActivity = null;
try {
Object activityThread = getActivityThread();
Field mActivitiesField = activityThread.getClass().getDeclaredField("mActivities");
mActivitiesField.setAccessible(true);
Object mActivities = mActivitiesField.get(activityThread);
if (!(mActivities instanceof Map)) {
return list;
}
Map<Object, Object> binder_activityClientRecord_map = (Map<Object, Object>) mActivities;
for (Object activityRecord : binder_activityClientRecord_map.values()) {
Class activityClientRecordClass = activityRecord.getClass();
Field activityField = activityClientRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(activityRecord);
if (topActivity == null) {
Field pausedField = activityClientRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
topActivity = activity;
} else {
list.add(activity);
}
} else {
list.add(activity);
}
}
} catch (Exception e) {
Log.e("UtilsActivityLifecycle", "getActivitiesByReflect: " + e.getMessage());
}
if (topActivity != null) {
list.addFirst(topActivity);
}
return list;
}
}
网友评论