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

Commit cb5f54e2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "[LayoutInflater] Use precompiled layouts if available"

parents 7b490e38 928bbac9
Loading
Loading
Loading
Loading
+182 −79
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
@@ -37,6 +38,9 @@ import android.widget.FrameLayout;

import com.android.internal.R;

import dalvik.system.PathClassLoader;
import java.io.File;
import java.lang.reflect.Method;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

@@ -71,6 +75,10 @@ public abstract class LayoutInflater {
    private static final String TAG = LayoutInflater.class.getSimpleName();
    private static final boolean DEBUG = false;

    private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY
        = "view.precompiled_layout_enabled";
    private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";

    /** Empty stack trace used to avoid log spam in re-throw exceptions. */
    private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];

@@ -92,6 +100,13 @@ public abstract class LayoutInflater {
    private Factory2 mPrivateFactory;
    private Filter mFilter;

    // Indicates whether we should try to inflate layouts using a precompiled layout instead of
    // inflating from the XML resource.
    private boolean mUseCompiledView;
    // This variable holds the classloader that will be used to look for precompiled layouts. The
    // The classloader includes the generated compiled_view.dex file.
    private ClassLoader mPrecompiledClassLoader;

    @UnsupportedAppUsage
    final Object[] mConstructorArgs = new Object[2];

@@ -214,6 +229,7 @@ public abstract class LayoutInflater {
     */
    protected LayoutInflater(Context context) {
        mContext = context;
        initPrecompiledViews();
    }

    /**
@@ -230,6 +246,7 @@ public abstract class LayoutInflater {
        mFactory2 = original.mFactory2;
        mPrivateFactory = original.mPrivateFactory;
        setFilter(original.mFilter);
        initPrecompiledViews();
    }

    /**
@@ -371,6 +388,29 @@ public abstract class LayoutInflater {
        }
    }

    private void initPrecompiledViews() {
        try {
            mUseCompiledView =
                SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false);
            if (mUseCompiledView) {
                mPrecompiledClassLoader = mContext.getClassLoader();
                String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
                if (new File(dexFile).exists()) {
                    mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
                } else {
                    // If the precompiled layout file doesn't exist, then disable precompiled
                    // layouts.
                    mUseCompiledView = false;
                }
            }
        } catch (Throwable e) {
            if (DEBUG) {
                Log.e(TAG, "Failed to initialized precompiled views layouts", e);
            }
            mUseCompiledView = false;
        }
    }

    /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
@@ -430,7 +470,11 @@ public abstract class LayoutInflater {
                  + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if (view != null) {
            return view;
        }
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
@@ -438,6 +482,73 @@ public abstract class LayoutInflater {
        }
    }

    private @Nullable
    View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
        boolean attachToRoot) {
        if (!mUseCompiledView) {
            return null;
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");

        // Try to inflate using a precompiled layout.
        String pkg = res.getResourcePackageName(resource);
        String layout = res.getResourceEntryName(resource);

        try {
            Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView");
            Method inflater = clazz.getMethod(layout, Context.class, int.class);
            View view = (View) inflater.invoke(null, mContext, resource);

            if (view != null && root != null) {
                // We were able to use the precompiled inflater, but now we need to do some work to
                // attach the view to the root correctly.
                XmlResourceParser parser = res.getLayout(resource);
                try {
                    AttributeSet attrs = Xml.asAttributeSet(parser);
                    advanceToRootNode(parser);
                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);

                    if (attachToRoot) {
                        root.addView(view, params);
                    } else {
                        view.setLayoutParams(params);
                    }
                } finally {
                    parser.close();
                }
            }

            return view;
        } catch (Throwable e) {
            if (DEBUG) {
                Log.e(TAG, "Failed to use precompiled view", e);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        return null;
    }

    /**
     * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
     * found.
     */
    private void advanceToRootNode(XmlPullParser parser)
        throws InflateException, IOException, XmlPullParserException {
        // Look for the root node.
        int type;
        while ((type = parser.next()) != XmlPullParser.START_TAG &&
            type != XmlPullParser.END_DOCUMENT) {
            // Empty
        }

        if (type != XmlPullParser.START_TAG) {
            throw new InflateException(parser.getPositionDescription()
                + ": No start tag found!");
        }
    }

    /**
     * Inflate a new view hierarchy from the specified XML node. Throws
     * {@link InflateException} if there is an error.
@@ -471,18 +582,7 @@ public abstract class LayoutInflater {
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                advanceToRootNode(parser);
                final String name = parser.getName();

                if (DEBUG) {
@@ -985,6 +1085,9 @@ public abstract class LayoutInflater {
                + "reference. The layout ID " + value + " is not valid.");
        }

        final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
            (ViewGroup) parent, /*attachToRoot=*/true);
        if (precompiled == null) {
            final XmlResourceParser childParser = context.getResources().getLayout(layout);

            try {
@@ -1060,7 +1163,7 @@ public abstract class LayoutInflater {
            } finally {
                childParser.close();
            }

        }
        LayoutInflater.consumeChildElements(parser);
    }