androidandroid-fragmentsadbdumpsys

How to get the full name of fragments using "adb shell dumpsys activity"


For a testing purpose, I use adb shell dumpsys activity <activity_name> to dump the information of the fragments in the foreground activity.

The problem is that the dumped fragment name only contains the class name rather than its full name (full name means package name + class name). For example, if the fragment's full name is aaa.bbb.ccc.MyFragment, the dumped fragment name will be MyFragment.

This is not a severe problem if the app is not obfuscated. However, when an app is obfuscated, the dumped fragment name will mostly be like a single character (e.g., a, b, c, ...). Therefore it's hard to distinguish two different fragments simply by their dumped names.

Why does the Android OS choose to only dump the class name? What are the difficulties of dumping the full name? Are there any other options that an adb user can do to get the full name of the fragments at runtime?


Solution

  • According to the Android source, the fragment's toString() uses DebugUtils.buildShortClassTag() to print the class name.

    Fragment.java

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            DebugUtils.buildShortClassTag(this, sb);
            if (mIndex >= 0) {
                sb.append(" #");
                sb.append(mIndex);
            }
            if (mFragmentId != 0) {
                sb.append(" id=0x");
                sb.append(Integer.toHexString(mFragmentId));
            }
            if (mTag != null) {
                sb.append(" ");
                sb.append(mTag);
            }
            sb.append('}');
            return sb.toString();
        }
    

    DebugUtils.java

        public static void buildShortClassTag(Object cls, StringBuilder out) {
            if (cls == null) {
                out.append("null");
            } else {
                String simpleName = cls.getClass().getSimpleName();
                if (simpleName == null || simpleName.isEmpty()) {
                    simpleName = cls.getClass().getName();
                    int end = simpleName.lastIndexOf('.');
                    if (end > 0) {
                        simpleName = simpleName.substring(end+1);
                    }
                }
                out.append(simpleName);
                out.append('{');
                out.append(Integer.toHexString(System.identityHashCode(cls)));
            }
        }
    

    We can see buildShortClassTag() uses cls.getClass().getSimpleName() to print the class name.

    In conclusion, we cannot get the full name of fragments unless we change Android framework's source code and build a customized OS. But this solution is not perfect because fragments in AndroidX has their own logics to print the fragment information. AndroidX libraries are packaged inside an apk and they are also obfuscated.