关于android布局优化之view的创建过程

首先我们需要查看view加载到屏幕的过程,只有知道他们是怎么加载的后我们才能进行针对性的优化,

搞清楚了加载流程,我们就掌握了优化的利器。我们先从源头进入,然后一步步的进入到OS内部看他们怎么进行处理的。

在我们创建界面Activity时,会在oncreat方法中把我们定义的layoutxml文件使用setContentView赋值给系统,让OS加载xml文件然后显示到屏幕中。

1,我们先看一下Activity.setContentView()的具体实现,

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}   

从代码中我们能看到Activity.setcontentView中主要做两个工作,一个是把xml文件进行上传,另外一个是进行actionbar的初始化,从这里我们能知道若是设置为notitlebar属性的话,启动的速度会稍微会一点点,今天我们主要是查看布局优化暂时不去考试actionbar相关的问题,让我们继续去查看getWindow().setContentView()方法,其中getWindow方法得到的是一个phoneWindow,在android中左右的控件的显示其根本就是显示在phoneWindow中,这是一个根,首先让我们看一下phoneWindow的定义,从代码我们能看到他extendsWindowwindow在我的理解中就是phone的屏幕窗口,由此我们可以知道phoneWindow这个是屏幕窗口的对外的接口。


public class PhoneWindow extends Window implements MenuBuilder.Callback
@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

代码中红色的部分就是我们需要了解的详细信息,也是我们创建activity时做走的流程,从这部分我们能看到最后还是使用mLayoutInflater进行创建view,我们继续进入LayoutInflater.inflate()


public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    if (DEBUG) {
        Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                + Integer.toHexString(resource) + ")");
    }

    final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}

从代码中我们能看到会根据resourceid得到一个XmlResourceParserxml解析工具,然后把这个得到的工具传入到inflate中,现在让我们进入到inflate的具体实现中去,因为inflate()比较长我们截取其中加载view的部分。


if (TAG_MERGE.equals(name)) {
        if (root == null || !attachToRoot) {
            throw new InflateException("<merge /> can be used only with a valid "
                    + "ViewGroup root and attachToRoot=true");
        }

        rInflate(parser, root, inflaterContext, attrs, false);
    } else {
        // Temp is the root view that was found in the xml
        final View temp = createViewFromTag(root, name, inflaterContext, attrs);

        ViewGroup.LayoutParams params = null;

        if (root != null) {
            if (DEBUG) {
                System.out.println("Creating params from root: " +
                        root);
            }
            // Create layout params that match root, if supplied
            params = root.generateLayoutParams(attrs);
            if (!attachToRoot) {
                // Set the layout params for temp if we are not
                // attaching. (If we are, we use addView, below)
                temp.setLayoutParams(params);
            }
        }

        if (DEBUG) {
            System.out.println("-----> start inflating children");
        }

        // Inflate all children under temp against its context.
        rInflateChildren(parser, temp, attrs, true);

        if (DEBUG) {
            System.out.println("-----> done inflating children");
        }

        // We are supposed to attach all the views we found (int temp)
        // to root. Do that now.
        if (root != null && attachToRoot) {
            root.addView(temp, params);
        }

        // Decide whether to return the root that was passed in or the
        // top view found in xml.
        if (root == null || !attachToRoot) {
            result = temp;
        }
    }

} catch (XmlPullParserException e) {
    final InflateException ie = new InflateException(e.getMessage(), e);
    ie.setStackTrace(EMPTY_STACK_TRACE);
    throw ie;
} catch (Exception e) {
    final InflateException ie = new InflateException(parser.getPositionDescription()
            + ": " + e.getMessage(), e);
    ie.setStackTrace(EMPTY_STACK_TRACE);
    throw ie;
} finally {
    // Don't retain static reference on context.
    mConstructorArgs[0] = lastContext;
    mConstructorArgs[1] = null;

    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

return result;

从代码中我们可以看到根据Tag的不同会执行代码,让我们先看下若是TAGTAG_MERGE的话会走到rInflate()中,让我们去看下rInflate()的具体实现,


void rInflate(XmlPullParser parser, View parent, Context context,
        AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

    final int depth = parser.getDepth();
    int type;
    boolean pendingRequestFocus = false;

    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)) {
            pendingRequestFocus = true;
            consumeChildElements(parser);
        } 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, context, parent, attrs);
        } else if (TAG_MERGE.equals(name)) {
            throw new InflateException("<merge /> must be the root element");
        } else {
            final View view = createViewFromTag(parent, name, context, attrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflateChildren(parser, view, attrs, true);
            viewGroup.addView(view, params);
        }
    }

    if (pendingRequestFocus) {
        parent.restoreDefaultFocus();
    }

    if (finishInflate) {
        parent.onFinishInflate();
    }
}

从代码中我们看到,在此方法中遍历所有的控件进行处理,根据tag的不同进行不同的处理或者返回,在这段代码中最重要的代码是:

final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);

创建view并加到viewGroup中,这个方法中主要处理的是merge控件相关的内容,有此我们可以推断在layout中若是有机会的话可以使用merge控件,并且尽量的只使用一层,这样在加载的时候可以直接创建view不会在做其他的处理。


然后让我们回过头来看非TAG_MERGE的情况下,是如何进行加载控件的,在代码中我们发现若是不是TAG_MERGE的情况下就调用函数createViewFromTag()方法创建一个view,并且若是root不为null的话就会加上

if (root != null && attachToRoot) {
    root.addView(temp, params);
}

其实其他所有的view创建都是在这里进行的使用递归的方式调用createViewFromTag()方法,这个就是view创建的过程。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值