以下的时间的单位都是ns(纳秒)
package com.chillingvan.samplecode.xml;
import android.app.Activity;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.Toast;
import com.chillingvan.samplecode.R;
public class XmlActivity extends Activity {
private static final String TAG = "XmlActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xml);
testInflatePerformance();
testInflateWithXmlParserPerformance();
testFindViewTime();
}
private void testInflatePerformance() {
long initTime = System.nanoTime();
int times = 50;
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
LayoutInflater.from(this).inflate(R.layout.activity_xml, null);
Log.d(TAG, "each parser time cost=" + (System.nanoTime() - eachInitTime ));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testInflatePerformance: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
private void testInflateWithXmlParserPerformance() {
long initTime = System.nanoTime();
// can only be setted to 1 because the parser cannot be reused.
// But the parser creating does not cost time..
// Because the parse time is the main part of the cost.
int times = 1;
XmlResourceParser xmlResourceParser = getResources().getLayout(R.layout.activity_xml);
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
LayoutInflater.from(this).inflate(xmlResourceParser, null);
Log.d(TAG, String.format("each parser time cost=%d ns",(System.nanoTime() - eachInitTime )));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testInflateWithXmlParserPerformance: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
private void testFindViewTime() {
long initTime = System.nanoTime();
int times = 40;
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
findViewById(R.id.xml_txt);
Log.d(TAG, String.format("each parser time cost=%d ns",(System.nanoTime() - eachInitTime )));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testFindViewTime: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
}
来看第一个inflate(int res, ViewGroup)方法的测试性能:
private void testInflatePerformance() {
long initTime = System.nanoTime();
int times = 50;
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
LayoutInflater.from(this).inflate(R.layout.activity_xml, null);
Log.d(TAG, "each parser time cost=" + (System.nanoTime() - eachInitTime ));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testInflatePerformance: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
可以看到每次inflate所花费的时间大致相同,说明 android 不会对重复 inflate 的数据进行缓存。
第二个方法:
private void testInflateWithXmlParserPerformance() {
long initTime = System.nanoTime();
// can only be setted to 1 because the parser cannot be reused.
// But the parser creating does not cost time..
// Because the parse time is the main part of the cost.
int times = 1;
XmlResourceParser xmlResourceParser = getResources().getLayout(R.layout.activity_xml);
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
LayoutInflater.from(this).inflate(xmlResourceParser, null);
Log.d(TAG, String.format("each find view time cost=%d ns",(System.nanoTime() - eachInitTime )));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testInflateWithXmlParserPerformance: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
inflate((XmlPullParser parser, ViewGroup root))这个方法传给inflate的参数是一个 XmlResourceParser。 原本是用来测试性能的,但是实际上测不了,因为获取到的parser只能用来解析一次,再调用一次就会报错。
inflate((XmlPullParser parser, ViewGroup root))其实是inflate (int resource, ViewGroup root)内部使用的方法,可以在源码上找到相应细节。
结果:
可以看到和第一个方法差别不大,也就是说Inflate的消耗时间注意不是由获取parser这一过程消耗的。
第三个方法:
private void testFindViewTime() {
long initTime = System.nanoTime();
int times = 40;
for (int i = 0; i < times; i++) {
long eachInitTime = System.nanoTime();
findViewById(R.id.xml_txt);
Log.d(TAG, String.format("each parser time cost=%d ns",(System.nanoTime() - eachInitTime )));
}
long timeCost = System.nanoTime() - initTime;
String log = String.format("testFindViewTime: %d times time cost=%d ns", times, timeCost);
Log.d(TAG, log);
Toast.makeText(XmlActivity.this, log, Toast.LENGTH_SHORT).show();
}
结果:
这个是测试findView的性能,从结果可以看到findView耗费的时间不多。
那么inflate方法花费的时间主要花在哪一部分?
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
*
* @param inheritContext Whether the root view should be inflated in its
* parent's context. This should be true when called inflating
* child views recursively, or false otherwise.
*/
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate, boolean inheritContext) throws XmlPullParserException,
IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
parseInclude(parser, parent, attrs, inheritContext);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else {
final View view = createViewFromTag(parent, name, attrs, inheritContext);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true, true);
viewGroup.addView(view, params);
}
}
if (finishInflate) parent.onFinishInflate();
}
猜测主要花费在rInflate方法这里,是一个递归方法。
最后,总结以下,inflate方法的性能瓶颈不在读磁盘,而在解析上,所以越view 层级复杂的layout越低性能。