Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d857b90 authored by Siva Velusamy's avatar Siva Velusamy
Browse files

Improve hierarchy viewer dump hierarchy latency

Hierarchy Viewer obtains the properties for each view by using
reflection and looking for fields and methods that have the
@ExportedProperty annotation. Using reflection made it quite slow
for large view hierarchies.

This CL adds a new method (encode) to each class that wishes to
export data to hiererachy viewer. Inside this method, the object
can write a sequence of key, value pairs corresponding to the
values it wants exported.

With this change, the dump hierarchy operation that used to take
more than 10 seconds can be performed in a few hundred milliseconds.

Change-Id: I199ac2e7ca3c59ebcfec7e6bd201e134c41fd583
parent 0a008049
Loading
Loading
Loading
Loading
+17 −2
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.StyleRes;
import android.annotation.StyleableRes;

import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;

@@ -62,6 +61,7 @@ import android.util.Pools.SynchronizedPool;
import android.util.Slog;
import android.util.TypedValue;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;

import java.io.IOException;
import java.io.InputStream;
@@ -1806,12 +1806,27 @@ public class Resources {
            for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
                final int resId = mKey.mResId[i];
                final boolean forced = mKey.mForce[i];
                try {
                    themes[i] = getResourceName(resId);
                } catch (NotFoundException e) {
                    themes[i] = Integer.toHexString(i);
                }
                themes[i + 1] = forced ? "forced" : "not forced";
            }
            return themes;
        }

        /** @hide */
        public void encode(@NonNull ViewHierarchyEncoder encoder) {
            encoder.beginObject(this);
            // TODO: revert after getTheme() is fixed
            String[] properties = new String[0]; // getTheme();
            for (int i = 0; i < properties.length; i += 2) {
                encoder.addProperty(properties[i], properties[i+1]);
            }
            encoder.endObject();
        }

        /**
         * Rebases the theme against the parent Resource object's current
         * configuration by re-applying the styles passed to
+13 −3
Original line number Diff line number Diff line
@@ -229,15 +229,25 @@ public class DdmHandleViewDebug extends ChunkHandler {
    private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
        boolean skipChildren = in.getInt() > 0;
        boolean includeProperties = in.getInt() > 0;
        boolean v2 = in.hasRemaining() && in.getInt() > 0;

        ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
        long start = System.currentTimeMillis();

        ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
        try {
            if (v2) {
                ViewDebug.dumpv2(rootView, b);
            } else {
                ViewDebug.dump(rootView, skipChildren, includeProperties, b);
        } catch (IOException e) {
            }
        } catch (IOException | InterruptedException e) {
            return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
                    + e.getMessage());
        }

        long end = System.currentTimeMillis();
        Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start));

        byte[] data = b.toByteArray();
        return new Chunk(CHUNK_VURT, data, 0, data.length);
    }
+134 −0
Original line number Diff line number Diff line
@@ -22353,4 +22353,138 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        final String output = bits + " " + name;
        found.put(key, output);
    }
    /** {@hide} */
    void encode(@NonNull ViewHierarchyEncoder stream) {
        stream.beginObject(this);
        encodeProperties(stream);
        stream.endObject();
    }
    /** {@hide} */
    @CallSuper
    protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
        Object resolveId = ViewDebug.resolveId(getContext(), mID);
        if (resolveId instanceof String) {
            stream.addProperty("id", (String) resolveId);
        } else {
            stream.addProperty("id", mID);
        }
        stream.addProperty("misc:transformation.alpha",
                mTransformationInfo != null ? mTransformationInfo.mAlpha : 0);
        stream.addProperty("misc:transitionName", getTransitionName());
        // layout
        stream.addProperty("layout:left", mLeft);
        stream.addProperty("layout:right", mRight);
        stream.addProperty("layout:top", mTop);
        stream.addProperty("layout:bottom", mBottom);
        stream.addProperty("layout:width", getWidth());
        stream.addProperty("layout:height", getHeight());
        stream.addProperty("layout:layoutDirection", getLayoutDirection());
        stream.addProperty("layout:layoutRtl", isLayoutRtl());
        stream.addProperty("layout:hasTransientState", hasTransientState());
        stream.addProperty("layout:baseline", getBaseline());
        // layout params
        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams != null) {
            stream.addPropertyKey("layoutParams");
            layoutParams.encode(stream);
        }
        // scrolling
        stream.addProperty("scrolling:scrollX", mScrollX);
        stream.addProperty("scrolling:scrollY", mScrollY);
        // padding
        stream.addProperty("padding:paddingLeft", mPaddingLeft);
        stream.addProperty("padding:paddingRight", mPaddingRight);
        stream.addProperty("padding:paddingTop", mPaddingTop);
        stream.addProperty("padding:paddingBottom", mPaddingBottom);
        stream.addProperty("padding:userPaddingRight", mUserPaddingRight);
        stream.addProperty("padding:userPaddingLeft", mUserPaddingLeft);
        stream.addProperty("padding:userPaddingBottom", mUserPaddingBottom);
        stream.addProperty("padding:userPaddingStart", mUserPaddingStart);
        stream.addProperty("padding:userPaddingEnd", mUserPaddingEnd);
        // measurement
        stream.addProperty("measurement:minHeight", mMinHeight);
        stream.addProperty("measurement:minWidth", mMinWidth);
        stream.addProperty("measurement:measuredWidth", mMeasuredWidth);
        stream.addProperty("measurement:measuredHeight", mMeasuredHeight);
        // drawing
        stream.addProperty("drawing:elevation", getElevation());
        stream.addProperty("drawing:translationX", getTranslationX());
        stream.addProperty("drawing:translationY", getTranslationY());
        stream.addProperty("drawing:translationZ", getTranslationZ());
        stream.addProperty("drawing:rotation", getRotation());
        stream.addProperty("drawing:rotationX", getRotationX());
        stream.addProperty("drawing:rotationY", getRotationY());
        stream.addProperty("drawing:scaleX", getScaleX());
        stream.addProperty("drawing:scaleY", getScaleY());
        stream.addProperty("drawing:pivotX", getPivotX());
        stream.addProperty("drawing:pivotY", getPivotY());
        stream.addProperty("drawing:opaque", isOpaque());
        stream.addProperty("drawing:alpha", getAlpha());
        stream.addProperty("drawing:transitionAlpha", getTransitionAlpha());
        stream.addProperty("drawing:shadow", hasShadow());
        stream.addProperty("drawing:solidColor", getSolidColor());
        stream.addProperty("drawing:layerType", mLayerType);
        stream.addProperty("drawing:willNotDraw", willNotDraw());
        stream.addProperty("drawing:hardwareAccelerated", isHardwareAccelerated());
        stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing());
        stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled());
        stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering());
        // focus
        stream.addProperty("focus:hasFocus", hasFocus());
        stream.addProperty("focus:isFocused", isFocused());
        stream.addProperty("focus:isFocusable", isFocusable());
        stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode());
        stream.addProperty("misc:clickable", isClickable());
        stream.addProperty("misc:pressed", isPressed());
        stream.addProperty("misc:selected", isSelected());
        stream.addProperty("misc:touchMode", isInTouchMode());
        stream.addProperty("misc:hovered", isHovered());
        stream.addProperty("misc:activated", isActivated());
        stream.addProperty("misc:visibility", getVisibility());
        stream.addProperty("misc:fitsSystemWindows", getFitsSystemWindows());
        stream.addProperty("misc:filterTouchesWhenObscured", getFilterTouchesWhenObscured());
        stream.addProperty("misc:enabled", isEnabled());
        stream.addProperty("misc:soundEffectsEnabled", isSoundEffectsEnabled());
        stream.addProperty("misc:hapticFeedbackEnabled", isHapticFeedbackEnabled());
        // theme attributes
        Resources.Theme theme = getContext().getTheme();
        if (theme != null) {
            stream.addPropertyKey("theme");
            theme.encode(stream);
        }
        // view attribute information
        int n = mAttributes != null ? mAttributes.length : 0;
        stream.addProperty("meta:__attrCount__", n/2);
        for (int i = 0; i < n; i += 2) {
            stream.addProperty("meta:__attr__" + mAttributes[i], mAttributes[i+1]);
        }
        stream.addProperty("misc:scrollBarStyle", getScrollBarStyle());
        // text
        stream.addProperty("text:textDirection", getTextDirection());
        stream.addProperty("text:textAlignment", getTextAlignment());
        // accessibility
        CharSequence contentDescription = getContentDescription();
        stream.addProperty("accessibility:contentDescription",
                contentDescription == null ? "" : contentDescription.toString());
        stream.addProperty("accessibility:labelFor", getLabelFor());
        stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility());
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -800,6 +801,7 @@ public class ViewDebug {

    /**
     * Dumps the view hierarchy starting from the given view.
     * @deprecated See {@link #dumpv2(View, ByteArrayOutputStream)} below.
     * @hide
     */
    public static void dump(View root, boolean skipChildren, boolean includeProperties,
@@ -824,6 +826,28 @@ public class ViewDebug {
        }
    }

    /**
     * Dumps the view hierarchy starting from the given view.
     * Rather than using reflection, it uses View's encode method to obtain all the properties.
     * @hide
     */
    public static void dumpv2(@NonNull final View view, @NonNull ByteArrayOutputStream out)
            throws InterruptedException {
        final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out);
        final CountDownLatch latch = new CountDownLatch(1);

        view.post(new Runnable() {
            @Override
            public void run() {
                view.encode(encoder);
                latch.countDown();
            }
        });

        latch.await(2, TimeUnit.SECONDS);
        encoder.endStream();
    }

    /**
     * Dumps the theme attributes from the given View.
     * @hide
+44 −0
Original line number Diff line number Diff line
@@ -6861,6 +6861,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            }
            return String.valueOf(size);
        }

        /** @hide */
        void encode(@NonNull ViewHierarchyEncoder encoder) {
            encoder.beginObject(this);
            encodeProperties(encoder);
            encoder.endObject();
        }

        /** @hide */
        protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
            encoder.addProperty("width", width);
            encoder.addProperty("height", height);
        }
    }

    /**
@@ -7329,6 +7342,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                    bottomMargin,
                    paint);
        }

        /** @hide */
        @Override
        protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
            super.encodeProperties(encoder);
            encoder.addProperty("leftMargin", leftMargin);
            encoder.addProperty("topMargin", topMargin);
            encoder.addProperty("rightMargin", rightMargin);
            encoder.addProperty("bottomMargin", bottomMargin);
            encoder.addProperty("startMargin", startMargin);
            encoder.addProperty("endMargin", endMargin);
        }
    }

    /* Describes a touched view and the ids of the pointers that it has captured.
@@ -7665,4 +7690,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

        canvas.drawLines(sDebugLines, paint);
    }

    /** @hide */
    @Override
    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
        super.encodeProperties(encoder);

        encoder.addProperty("focus:descendantFocusability", getDescendantFocusability());
        encoder.addProperty("drawing:clipChildren", getClipChildren());
        encoder.addProperty("drawing:clipToPadding", getClipToPadding());
        encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
        encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());

        int n = getChildCount();
        encoder.addProperty("meta:__childCount__", (short)n);
        for (int i = 0; i < n; i++) {
            encoder.addPropertyKey("meta:__child__" + i);
            getChildAt(i).encode(encoder);
        }
    }
}
Loading