------2018-07-26更新-----
一些說明:
其實對于下文的拿Activity引用的方式我個人已經(jīng)不推薦了。這里僅作為一種思路萍倡,同時解答一些類似Activity創(chuàng)建后,到底被哪個對象持有等類似的問題辟汰。
不關(guān)心廢話列敲,直接需要結(jié)果的,可以參考這里:https://github.com/aesean/ActivityStack
由于各種各樣的原因帖汞,我們項目里部分地方需要用到獲取當(dāng)前App的所有Activity列表戴而。我們之前的做法是比較簡單粗暴的,就是Application里維護一個Activity的弱引用List翩蘸,每次創(chuàng)建Activity的時候把Activity的實例Add到List里所意。直覺告訴我這樣寫很不好,但是項目里用這種方式代碼也一直跑的好好的沒出什么問題催首,泄漏的地方該處理的也都處理了扶踊。直覺告訴我,F(xiàn)ramework里肯定某個地方持有了所有的Activity郎任。類似android.app.FragmentManager并沒有像supportFragmentManager那樣提供getFragments方法秧耗,但是我們知道其實只是android.app.FragmentManager沒有把mActive public而已(原因就只有Google自己知道了),自己反射是很容易拿到mActive引用的舶治。
最近在看Framewok層源碼的時候看到這樣一個東西分井。
public final class ActivityThread {
......
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
......
}
ActivityThread里持有了一個Map,這個Map的Value是ActivityClientRecord霉猛,熟悉的朋友一定知道ActivityClientRecord是持有一個Activity對象的尺锚,然后相當(dāng)于是一個Activity被一個Map持有。那mActivities的value很可能就是整個Activity列表惜浅。
先不急著反射瘫辩,先debug看下。
太長了,拉到右邊
AllActivityActivity和ActivityThreadActivity剛好是兩個Activity伐厌。反射那實例前還有個問題阅仔,當(dāng)前是從Activity對象里的mMainThread對象的mActivities。實際中我們肯定是希望這個方法應(yīng)該可以從Application對象里就可以調(diào)用弧械,否則局限性太明顯了。
繼續(xù)debug看下Application都有什么屬性空民。
很容易就發(fā)現(xiàn)了mLoadedApk持有ActivityThread@4446刃唐,而且兩個都是@4446這表示是同一個實例,那剩下就簡單了界轩,一路反射過去拿到Map拿到ValueList画饥。
private static List<Activity> getActivitiesByApplication(Application application) {
List<Activity> list = new ArrayList<>();
try {
Class<Application> applicationClass = Application.class;
Field mLoadedApkField = applicationClass.getDeclaredField("mLoadedApk");
mLoadedApkField.setAccessible(true);
Object mLoadedApk = mLoadedApkField.get(application);
Class<?> mLoadedApkClass = mLoadedApk.getClass();
Field mActivityThreadField = mLoadedApkClass.getDeclaredField("mActivityThread");
mActivityThreadField.setAccessible(true);
Object mActivityThread = mActivityThreadField.get(mLoadedApk);
Class<?> mActivityThreadClass = mActivityThread.getClass();
Field mActivitiesField = mActivityThreadClass.getDeclaredField("mActivities");
mActivitiesField.setAccessible(true);
Object mActivities = mActivitiesField.get(mActivityThread);
// 注意這里一定寫成Map,低版本這里用的是HashMap浊猾,高版本用的是ArrayMap
if (mActivities instanceof Map) {
@SuppressWarnings("unchecked")
Map<Object, Object> arrayMap = (Map<Object, Object>) mActivities;
for (Map.Entry<Object, Object> entry : arrayMap.entrySet()) {
Object value = entry.getValue();
Class<?> activityClientRecordClass = value.getClass();
Field activityField = activityClientRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Object o = activityField.get(value);
list.add((Activity) o);
}
}
} catch (Exception e) {
e.printStackTrace();
list = null;
}
return list;
}
方法設(shè)置為了private抖甘,是否需要處理持有Activity導(dǎo)致的回收問題,是否要包裝成弱引用List葫慎,那就看你自己的需要了衔彻。
當(dāng)然最后有個問題,我只是在5.1和7.0的手機上測試了下沒問題偷办,因為是反射艰额,并不是PublicApi,低版本是否有不一樣的實現(xiàn)椒涯,請自行測試柄沮。另外還有不同的啟動模式是否會有影響,也請自行測試废岂。
上面反射的過程比較復(fù)雜祖搓,代碼非常雜亂,所以寫個工具類湖苞,簡化下反射過長拯欧。具體參考這里。
https://github.com/aesean/ActivityStack/blob/master/app/src/main/java/com/aesean/activitystack/utils/ReflectUtils.java
簡化后可以直接使用這個類進行反射獲取袒啼。
https://github.com/aesean/ActivityStack/blob/master/app/src/main/java/com/aesean/activitystack/utils/ApplicationUtils.java
另外更加詳細的參考這里:
http://blog.csdn.net/qinjuning/article/details/7262769
和這里:
http://blog.csdn.net/xieqibao/article/details/6570080