利用反射拿到Android的整个Activity栈。

匿名 (未验证) 提交于 2019-12-03 00:36:02

转自https://www.jianshu.com/p/ac0b237bac03

利用反射拿到Android的整个Activity栈。

Jerry2015
2016.11.21 21:25*字数 763阅读 4306评论 6赞赏 1

不关心废话,直接需要结果的,可以参考这里:https://github.com/aesean/ActivityStack
由于各种各样的原因,我们项目里部分地方需要用到获取当前App的所有Activity列表。我们之前的做法是比较简单粗暴的,就是Application里维护一个Activity的弱引用List,每次创建Activity的时候把Activity的实例Add到List里。直觉告诉我这样写很不好,但是项目里用这种方式代码也一直跑的好好的没出什么问题,泄漏的地方该处理的也都处理了。直觉告诉我,Framework里肯定某个地方持有了所有的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对象的,然后相当于是一个Activity被一个Map持有。那mActivities的value很可能就是整个Activity列表。
先不急着反射,先debug看下。

Debug

太长了,拉到右边

Debug

AllActivityActivity和ActivityThreadActivity刚好是两个Activity。反射那实例前还有个问题,当前是从Activity对象里的mMainThread对象的mActivities。实际中我们肯定是希望这个方法应该可以从Application对象里就可以调用,否则局限性太明显了。
继续debug看下Application都有什么属性。

Debug

很容易就发现了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;     } 

方法设置为了private,是否需要处理持有Activity导致的回收问题,是否要包装成弱引用List,那就看你自己的需要了。
当然最后有个问题,我只是在5.1和7.0的手机上测试了下没问题,因为是反射,并不是PublicApi,低版本是否有不一样的实现,请自行测试。另外还有不同的启动模式是否会有影响,也请自行测试。
上面反射的过程比较复杂,代码非常杂乱,所以写个工具类,简化下反射过长。具体参考这里。
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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!