獲取棧頂 TopActivity 方法匯總

最近項(xiàng)目中遇到一個(gè)問題斤寂,需要做一個(gè)全局的彈窗丘侠,為此需要拿到當(dāng)前棧頂?shù)?Activity。在這里做一個(gè)總結(jié):

  1. ActivityManager.getRunningTasks()
    這個(gè)函數(shù)是官方的 public API句狼,但已經(jīng)廢棄粱快,原因是:“可能造成隱私泄露”。但因?yàn)榧嫒菪栽蜓匮匀粫?huì)返回一小部分不敏感的數(shù)據(jù)览徒,比如當(dāng)前進(jìn)程的 Activities 或者 Launcher。
    換句話說颂龙,在 Android L 以上习蓬,返回結(jié)果非常不可靠,而且只能拿到ComponentName 而不是 Activity 實(shí)例措嵌,不建議使用躲叼。
public static ComponentName getTopActivity(Context c) {
        ActivityManager am = (ActivityManager) c
                .getSystemService(Context.ACTIVITY_SERVICE);
        if (am != null) {
            List<ActivityManager.RunningTaskInfo> infos
                    = am.getRunningTasks(1);
            if (infos != null && infos.size() != 0) {
                return infos.get(0).topActivity;
            }
        }
        return null;
    }
  1. BaseActivity 記錄生命周期回調(diào)
    這個(gè)方法對(duì)于沒有繼承 BaseActivity 的 Activity 是無效的……由此可見在項(xiàng)目開始時(shí)創(chuàng)建基類成員并寫進(jìn)開發(fā)規(guī)范多么重要……

    public abstract class BaseActivity extends AppCompatActivity {
        private static WeakReference<BaseActivity> sTopActivity;

        public static BaseActivity getTopActivity() {
            return sTopActivity == null ? null : sTopActivity.get();
        }

        @Override
        protected void onStart() {
            super.onStart();
            sTopActivity = new WeakReference<>(this);
        }

        @Override
        protected void onStop() {
            super.onStop();
            if (getTopActivity() == this) {
                sTopActivity = null;
            }
        }
    }
  1. Application.registerActivityLifecycleCallback
    這個(gè)方法可以在 Application 的 onCreate 函數(shù)中注冊(cè)
        private static WeakReference<Activity> sTopActivity;

        public static Activity getTopActivity() {
            return sTopActivity == null ? null : sTopActivity.get();
        }

        @Override
        public void onCreate() {
            super.onCreate();

            registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

                }

                @Override
                public void onActivityStarted(Activity activity) {
                    sTopActivity = new WeakReference<>(activity);
                }

                @Override
                public void onActivityResumed(Activity activity) {

                }

                @Override
                public void onActivityPaused(Activity activity) {

                }

                @Override
                public void onActivityStopped(Activity activity) {
                    if (activity == getTopActivity()) {
                        sTopActivity = null;
                    }
                }

                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

                }

                @Override
                public void onActivityDestroyed(Activity activity) {

                }
            });
        }
  1. ActivityThread.ActivityClientRecord
    這個(gè)方法是實(shí)際我使用的,因?yàn)轫?xiàng)目是 Xposed 模塊企巢,可能是本機(jī)代碼優(yōu)化的原因枫慷,Application.onCreate() 函數(shù) hook 不到,就算 hook 到了注冊(cè)成功也收不到回調(diào)浪规,有點(diǎn)莫名其妙或听。如果看過 ActivityThread 的源碼,會(huì)發(fā)現(xiàn)里面有一個(gè)內(nèi)部類 ActivityClientRecord笋婿,這個(gè)類是用來保存本進(jìn)程所有 Activity 實(shí)例及生命狀態(tài)的誉裆。缺點(diǎn)是:由于使用了反射和內(nèi)部類,兼容性無法保證缸濒,如果 ROM 廠商改個(gè)名字可能就跪了足丢。而且 Android 9 以上限制了反射的使用,需要進(jìn)行一下適配庇配。
        private boolean mIgnore = false;
        private Field mActivityField;
        private Field mActivitiesField;
        private Field mStoppedField;

        @SuppressWarnings("unchecked")
        private Activity getTopActivity() {
            if (mIgnore) {
                return null;
            }
            try {
                if (mActivitiesField == null) {
                    mActivitiesField = ReflectUtils.getDeclaredField(
                            ActivityThread.class, "mActivities"
                    );
                    Class<?> cls = Class.forName("android.app.ActivityThread$ActivityClientRecord");
                    mActivityField = ReflectUtils.getDeclaredField(cls, "activity");
                    mStoppedField = ReflectUtils.getDeclaredField(cls, "stopped");
                }

                ActivityThread thread = ActivityThread.currentActivityThread();
                final Map<Object, Object> mActivities = (Map<Object, Object>)
                        mActivitiesField.get(thread);

                for (Object r : mActivities.values()) {
                    Activity activity = (Activity) mActivityField.get(r);
                    if (activity.isFinishing() || activity.isDestroyed()
                        || ((boolean) mStoppedField.get(r))) {
                        continue;
                    }
                    return activity;
                }
            } catch (Exception e) {
                mIgnore = true;
                Log.e(TAG, "getTopActivity: ", e);
            }
            return null;
        }

下面是 ReflectUtils

public static Field getDeclaredField(Class<?> cls, String name) {
        Field field;
        try {
            field = cls.getDeclaredField(name);
            field.setAccessible(true);
        } catch (NoSuchFieldException e) {
            field = null;
            Log.e(TAG, "getDeclaredField: " + name, e);
        }
        return field;
    }

對(duì)于 Android9 以上斩跌,可以參考
http://weishu.me/2019/03/16/another-free-reflection-above-android-p/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市捞慌,隨后出現(xiàn)的幾起案子耀鸦,更是在濱河造成了極大的恐慌,老刑警劉巖卿闹,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揭糕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锻霎,警方通過查閱死者的電腦和手機(jī)著角,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旋恼,“玉大人吏口,你說我怎么就攤上這事奄容。” “怎么了产徊?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵昂勒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我舟铜,道長(zhǎng)戈盈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任谆刨,我火速辦了婚禮塘娶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痊夭。我一直安慰自己刁岸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布她我。 她就那樣靜靜地躺著虹曙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪番舆。 梳的紋絲不亂的頭發(fā)上酝碳,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音恨狈,去河邊找鬼击敌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拴事,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播圣蝎,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼刃宵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了徘公?” 一聲冷哼從身側(cè)響起牲证,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎关面,沒想到半個(gè)月后坦袍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡等太,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年捂齐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缩抡。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奠宜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情压真,我是刑警寧澤娩嚼,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站滴肿,受9級(jí)特大地震影響岳悟,放射性物質(zhì)發(fā)生泄漏绷柒。R本人自食惡果不足惜伶授,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窄坦。 院中可真熱鬧拴驮,春花似錦春瞬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至潜沦,卻和暖如春萄涯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唆鸡。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工涝影, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人争占。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓燃逻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親臂痕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伯襟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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