Android layout Inflate 性能浅析

Android layout Inflate 性能浅析

本文从三个测试方法测试 android 的 LayoutInflater.inflate 函数的性能。先上代码;

以下的时间的单位都是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同一个layout文件50次的结果如下:

可以看到每次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("<include /> cannot be the root element");
                }
                parseInclude(parser, parent, attrs, inheritContext);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> 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越低性能。

猜你喜欢

转载自blog.csdn.net/fzl562410663/article/details/50932337