記錄一次UtilsActivityLifecycleImpl必現(xiàn)內(nèi)存泄漏

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 包名


圖片證據(jù)
0x2.分析內(nèi)存hprof文件com.blankj.utilcode.util.UtilsActivityLifecycleImpl.mActivityList持有activity無法釋放
內(nèi)存泄漏
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無法被釋放
image.png
0x4.結(jié)論.Activity activity = ActivityUtils.getTopActivity();//請注意該函數(shù)調(diào)用時(shí)機(jī)!
image.png
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;
    }
}
0x6.調(diào)用LeakHelper.getTopActivity() 繞過UtilsActivityLifecycleImpl因?yàn)檎{(diào)用時(shí)機(jī)不正確引起的內(nèi)存泄漏
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匙监,一起剝皮案震驚了整個(gè)濱河市宵睦,隨后出現(xiàn)的幾起案子饵沧,更是在濱河造成了極大的恐慌崇猫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渣刷,死亡現(xiàn)場離奇詭異苦锨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)药磺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門告组,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人癌佩,你說我怎么就攤上這事木缝。” “怎么了围辙?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵氨肌,是天一觀的道長。 經(jīng)常有香客問我酌畜,道長怎囚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮恳守,結(jié)果婚禮上考婴,老公的妹妹穿的比我還像新娘。我一直安慰自己催烘,他們只是感情好沥阱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伊群,像睡著了一般考杉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舰始,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天崇棠,我揣著相機(jī)與錄音,去河邊找鬼丸卷。 笑死枕稀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谜嫉。 我是一名探鬼主播萎坷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沐兰!你這毒婦竟也來了哆档?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤住闯,失蹤者是張志新(化名)和其女友劉穎瓜浸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寞秃,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斟叼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了春寿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朗涩。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绑改,靈堂內(nèi)的尸體忽然破棺而出谢床,到底是詐尸還是另有隱情,我是刑警寧澤厘线,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布识腿,位于F島的核電站,受9級特大地震影響造壮,放射性物質(zhì)發(fā)生泄漏渡讼。R本人自食惡果不足惜骂束,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望成箫。 院中可真熱鬧展箱,春花似錦、人聲如沸蹬昌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皂贩。三九已至栖榨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間明刷,已是汗流浹背婴栽。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遮精,地道東北人居夹。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓败潦,卻偏偏與公主長得像本冲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子劫扒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容