Activity启动界面绘制

作者: zhangxuanchen | 来源:发表于2017-02-11 14:54 被阅读11次

    入口就从这个setContentView方法进入了

    public class DemoActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            super.setContentView(R.layout.view_demo);
        }
    }
    

    Activity中的setContentView方法

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

    getWindow()获取的是 com.android.internal.policy.impl.PhoneWindow
    这个对象是PhoneWindow中的方法。

        @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();
            } else {
                mContentParent.removeAllViews();
            }
            mLayoutInflater.inflate(layoutResID, mContentParent);
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    installDecor初始化DecorView,DecorView是什么呢?
    布局文件如下

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_demo_root"
    android:orientation="vertical"              
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    </LinearLayout>
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            super.setContentView(R.layout.view_demo);
            View root_view = findViewById(R.id.view_demo_root);
            while (root_view != null) {
                Log.d("ViewDemo", root_view.getClass().toString());
                root_view = (View) root_view.getParent();
            }
    }
    

    Log打印:

    class android.widget.LinearLayout
    class android.widget.FrameLayout
    class com.android.internal.widget.ActionBarOverlayLayout
    class com.android.internal.policy.impl.PhoneWindow$DecorView
    

    从代码中可以看到DecorView为所有组件的父容器

    下面进入mLayoutInflater.inflate(layoutResID, mContentParent)方法中

    // 为节省篇幅,删除一些调试代码 
    public View inflate(int resource, ViewGroup root) {
        return inflate(resource, root, root != null);
    }
    
    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
    XmlResourceParser parser = getContext().getResources().getLayout(resource);
    try {
          return inflate(parser, root, attachToRoot);
    } finally {
          parser.close();
    }
    }
    
    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
            synchronized (mConstructorArgs) {
                final AttributeSet attrs = Xml.asAttributeSet(parser);
                Context lastContext = (Context)mConstructorArgs[0];
                mConstructorArgs[0] = mContext;
                View result = root;
                try {
                    // 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!");
               }
               final String name = parser.getName();
               // 根标签为merge时,root不能为空
               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, attrs, false);
                    } else {
                        // Temp is the root view that was found in the xml
                        View temp;
                        if (TAG_1995.equals(name)) {
                            temp = new BlinkLayout(mContext, attrs);
                        } else {
                            temp = createViewFromTag(root, name, attrs);
                        }
    
                        ViewGroup.LayoutParams params = null;
    
                        if (root != null) {
                            // 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);
                            }
                        }
    
                        // 解析temp的所有子View
                        rInflate(parser, temp, attrs, true);
    
                        // 将temp添加到root中
                        if (root != null && attachToRoot) {
                            root.addView(temp, params);
                        }
    
                        // 如果未指定root或者不附加到root,则返回xml所代表的view;
                        if (root == null || !attachToRoot) {
                            result = temp;
                        }
                    }
                } catch (XmlPullParserException e) {
                    InflateException ex = new InflateException(e.getMessage());
                    ex.initCause(e);
                    throw ex;
                } catch (IOException e) {
                    InflateException ex = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage());
                    ex.initCause(e);
                    throw ex;
                } finally {
                    // Don't retain static reference on context.
                    mConstructorArgs[0] = lastContext;
                    mConstructorArgs[1] = null;
                }
                return result;
            }
        }
    

    可以看到最终都调用了rInflate

    void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
    boolean finishInflate) 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_INCLUDE.equals(name)) {
                    if (parser.getDepth() == 0) {
                        throw new InflateException("<include /> cannot be the root element");
                    }
                    parseInclude(parser, parent, attrs);
                } else if (TAG_MERGE.equals(name)) {
                    throw new InflateException("<merge /> must be the root element");
                } else if (TAG_1995.equals(name)) {
                    final View view = new BlinkLayout(mContext, attrs);
                    final ViewGroup viewGroup = (ViewGroup) parent;
                    final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                    rInflate(parser, view, attrs, true);
                    viewGroup.addView(view, params);                
                } else {
                    final View view = createViewFromTag(parent, name, attrs);
                    final ViewGroup viewGroup = (ViewGroup) parent;
                    final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                    rInflate(parser, view, attrs, true);
                    viewGroup.addView(view, params);
                }
            }
            if (finishInflate) parent.onFinishInflate();
        }
    

    rInflate通过XmlPullParser解析出XML里所有的组件,并使用递归的方式逐层去获取子组件。看到这里我们就知道为什么不建议在View中做过多的嵌套了吧,因为递归调用会占用大量的栈帧,导致内存使用飙高,这样程序会栈溢出和内存溢出的风险。
    最终 rInflate 调用 createViewFromTag 方法生成了view 并加载到 DecorView中。

    最后通知Callback,ContentView发生改变

    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
    }
    

    在Activity方法attach中可以发现setCallback就是Activity本身

     final void attach(...) {
            ...
            mWindow = PolicyManager.makeNewWindow(this);
            mWindow.setCallback(this);
            ...
        }
        public void onContentChanged() {
        }
    

    界面发生变化后会调用Activity重写的onContentChanged,通知界面已经发生了改变。

    相关文章

      网友评论

        本文标题:Activity启动界面绘制

        本文链接:https://www.haomeiwen.com/subject/liigittx.html