美文网首页Android 开发
利用反射拿到Android的整个Activity栈。

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

作者: Jerry2015 | 来源:发表于2016-11-21 21:25 被阅读6180次

    ------2018-07-26更新-----
    一些说明:
    其实对于下文的拿Activity引用的方式我个人已经不推荐了。这里仅作为一种思路,同时解答一些类似Activity创建后,到底被哪个对象持有等类似的问题。


    不关心废话,直接需要结果的,可以参考这里: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

    相关文章

      网友评论

        本文标题:利用反射拿到Android的整个Activity栈。

        本文链接:https://www.haomeiwen.com/subject/rciupttx.html