0x0.必現(xiàn)步驟:打開app,退出app.內(nèi)存泄漏檢測工具LeakCanary必報(bào).
0x1.LeakCanary就一定準(zhǔn)嗎?不見得.秉著盡信它不如不用它原則.啟用adb校驗(yàn).如圖所示,Activities數(shù)量一直恒定,那內(nèi)存泄漏真心沒跑了.
輸入 adb shell dumpsys meminfo -a 包名
0x2.分析內(nèi)存hprof文件com.blankj.utilcode.util.UtilsActivityLifecycleImpl.mActivityList持有activity無法釋放
0x3.debug斷點(diǎn)UtilsActivityLifecycleImpl.mActivityList 發(fā)現(xiàn)有兩處.1是在生命周期onActivityCreated會被賦值2是getTopActivity時(shí)有機(jī)會會被賦值.為什么叫有機(jī)會呢?當(dāng)app關(guān)閉時(shí),生命周期正常分發(fā)mActivityList會被清空,此時(shí)調(diào)用getTopActivity就會被賦值,從而引起mActivityList持有activity無法被釋放
0x4.結(jié)論.Activity activity = ActivityUtils.getTopActivity();//請注意該函數(shù)調(diào)用時(shí)機(jī)!
0x5.如上圖所示.我只想獲取棧頂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;
}
}