Merge "[LayoutInflater] Use precompiled layouts if available" am: cb5f54e264
am: 3be49488d5
Change-Id: I2e714604fa13e9421987c57913bb099349172d45
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.
|
||||||
@@ -427,10 +467,14 @@ public abstract class LayoutInflater {
|
|||||||
final Resources res = getContext().getResources();
|
final Resources res = getContext().getResources();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
|
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
|
||||||
+ 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,82 +1085,85 @@ public abstract class LayoutInflater {
|
|||||||
+ "reference. The layout ID " + value + " is not valid.");
|
+ "reference. The layout ID " + value + " is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final XmlResourceParser childParser = context.getResources().getLayout(layout);
|
final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
|
||||||
|
(ViewGroup) parent, /*attachToRoot=*/true);
|
||||||
|
if (precompiled == null) {
|
||||||
|
final XmlResourceParser childParser = context.getResources().getLayout(layout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
|
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
|
||||||
|
|
||||||
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
|
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
|
||||||
type != XmlPullParser.END_DOCUMENT) {
|
type != XmlPullParser.END_DOCUMENT) {
|
||||||
// Empty.
|
// Empty.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != XmlPullParser.START_TAG) {
|
||||||
|
throw new InflateException(childParser.getPositionDescription() +
|
||||||
|
": No start tag found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String childName = childParser.getName();
|
||||||
|
|
||||||
|
if (TAG_MERGE.equals(childName)) {
|
||||||
|
// The <merge> tag doesn't support android:theme, so
|
||||||
|
// nothing special to do here.
|
||||||
|
rInflate(childParser, parent, context, childAttrs, false);
|
||||||
|
} else {
|
||||||
|
final View view = createViewFromTag(parent, childName,
|
||||||
|
context, childAttrs, hasThemeOverride);
|
||||||
|
final ViewGroup group = (ViewGroup) parent;
|
||||||
|
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(
|
||||||
|
attrs, R.styleable.Include);
|
||||||
|
final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
|
||||||
|
final int visibility = a.getInt(R.styleable.Include_visibility, -1);
|
||||||
|
a.recycle();
|
||||||
|
|
||||||
|
// We try to load the layout params set in the <include /> tag.
|
||||||
|
// If the parent can't generate layout params (ex. missing width
|
||||||
|
// or height for the framework ViewGroups, though this is not
|
||||||
|
// necessarily true of all ViewGroups) then we expect it to throw
|
||||||
|
// a runtime exception.
|
||||||
|
// We catch this exception and set localParams accordingly: true
|
||||||
|
// means we successfully loaded layout params from the <include>
|
||||||
|
// tag, false means we need to rely on the included layout params.
|
||||||
|
ViewGroup.LayoutParams params = null;
|
||||||
|
try {
|
||||||
|
params = group.generateLayoutParams(attrs);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// Ignore, just fail over to child attrs.
|
||||||
|
}
|
||||||
|
if (params == null) {
|
||||||
|
params = group.generateLayoutParams(childAttrs);
|
||||||
|
}
|
||||||
|
view.setLayoutParams(params);
|
||||||
|
|
||||||
|
// Inflate all children.
|
||||||
|
rInflateChildren(childParser, view, childAttrs, true);
|
||||||
|
|
||||||
|
if (id != View.NO_ID) {
|
||||||
|
view.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (visibility) {
|
||||||
|
case 0:
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
view.setVisibility(View.INVISIBLE);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
group.addView(view);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
childParser.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != XmlPullParser.START_TAG) {
|
|
||||||
throw new InflateException(childParser.getPositionDescription() +
|
|
||||||
": No start tag found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String childName = childParser.getName();
|
|
||||||
|
|
||||||
if (TAG_MERGE.equals(childName)) {
|
|
||||||
// The <merge> tag doesn't support android:theme, so
|
|
||||||
// nothing special to do here.
|
|
||||||
rInflate(childParser, parent, context, childAttrs, false);
|
|
||||||
} else {
|
|
||||||
final View view = createViewFromTag(parent, childName,
|
|
||||||
context, childAttrs, hasThemeOverride);
|
|
||||||
final ViewGroup group = (ViewGroup) parent;
|
|
||||||
|
|
||||||
final TypedArray a = context.obtainStyledAttributes(
|
|
||||||
attrs, R.styleable.Include);
|
|
||||||
final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
|
|
||||||
final int visibility = a.getInt(R.styleable.Include_visibility, -1);
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
// We try to load the layout params set in the <include /> tag.
|
|
||||||
// If the parent can't generate layout params (ex. missing width
|
|
||||||
// or height for the framework ViewGroups, though this is not
|
|
||||||
// necessarily true of all ViewGroups) then we expect it to throw
|
|
||||||
// a runtime exception.
|
|
||||||
// We catch this exception and set localParams accordingly: true
|
|
||||||
// means we successfully loaded layout params from the <include>
|
|
||||||
// tag, false means we need to rely on the included layout params.
|
|
||||||
ViewGroup.LayoutParams params = null;
|
|
||||||
try {
|
|
||||||
params = group.generateLayoutParams(attrs);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// Ignore, just fail over to child attrs.
|
|
||||||
}
|
|
||||||
if (params == null) {
|
|
||||||
params = group.generateLayoutParams(childAttrs);
|
|
||||||
}
|
|
||||||
view.setLayoutParams(params);
|
|
||||||
|
|
||||||
// Inflate all children.
|
|
||||||
rInflateChildren(childParser, view, childAttrs, true);
|
|
||||||
|
|
||||||
if (id != View.NO_ID) {
|
|
||||||
view.setId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (visibility) {
|
|
||||||
case 0:
|
|
||||||
view.setVisibility(View.VISIBLE);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
view.setVisibility(View.INVISIBLE);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
view.setVisibility(View.GONE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
group.addView(view);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
childParser.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutInflater.consumeChildElements(parser);
|
LayoutInflater.consumeChildElements(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user