Merge "[LayoutInflater] Use precompiled layouts if available"
This commit is contained in:
@@ -28,6 +28,7 @@ import android.content.res.XmlResourceParser;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -37,6 +38,9 @@ import android.widget.FrameLayout;
|
|||||||
|
|
||||||
import com.android.internal.R;
|
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.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@@ -71,6 +75,10 @@ public abstract class LayoutInflater {
|
|||||||
private static final String TAG = LayoutInflater.class.getSimpleName();
|
private static final String TAG = LayoutInflater.class.getSimpleName();
|
||||||
private static final boolean DEBUG = false;
|
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. */
|
/** Empty stack trace used to avoid log spam in re-throw exceptions. */
|
||||||
private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
|
private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
|
||||||
|
|
||||||
@@ -92,6 +100,13 @@ public abstract class LayoutInflater {
|
|||||||
private Factory2 mPrivateFactory;
|
private Factory2 mPrivateFactory;
|
||||||
private Filter mFilter;
|
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
|
@UnsupportedAppUsage
|
||||||
final Object[] mConstructorArgs = new Object[2];
|
final Object[] mConstructorArgs = new Object[2];
|
||||||
|
|
||||||
@@ -214,6 +229,7 @@ public abstract class LayoutInflater {
|
|||||||
*/
|
*/
|
||||||
protected LayoutInflater(Context context) {
|
protected LayoutInflater(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
initPrecompiledViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,6 +246,7 @@ public abstract class LayoutInflater {
|
|||||||
mFactory2 = original.mFactory2;
|
mFactory2 = original.mFactory2;
|
||||||
mPrivateFactory = original.mPrivateFactory;
|
mPrivateFactory = original.mPrivateFactory;
|
||||||
setFilter(original.mFilter);
|
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
|
* Inflate a new view hierarchy from the specified xml resource. Throws
|
||||||
* {@link InflateException} if there is an error.
|
* {@link InflateException} if there is an error.
|
||||||
@@ -430,7 +470,11 @@ public abstract class LayoutInflater {
|
|||||||
+ Integer.toHexString(resource) + ")");
|
+ 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 {
|
try {
|
||||||
return inflate(parser, root, attachToRoot);
|
return inflate(parser, root, attachToRoot);
|
||||||
} finally {
|
} 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
|
* Inflate a new view hierarchy from the specified XML node. Throws
|
||||||
* {@link InflateException} if there is an error.
|
* {@link InflateException} if there is an error.
|
||||||
@@ -471,18 +582,7 @@ public abstract class LayoutInflater {
|
|||||||
View result = root;
|
View result = root;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Look for the root node.
|
advanceToRootNode(parser);
|
||||||
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!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String name = parser.getName();
|
final String name = parser.getName();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -985,6 +1085,9 @@ public abstract class LayoutInflater {
|
|||||||
+ "reference. The layout ID " + value + " is not valid.");
|
+ "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);
|
final XmlResourceParser childParser = context.getResources().getLayout(layout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1060,7 +1163,7 @@ public abstract class LayoutInflater {
|
|||||||
} finally {
|
} finally {
|
||||||
childParser.close();
|
childParser.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
LayoutInflater.consumeChildElements(parser);
|
LayoutInflater.consumeChildElements(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user