zoukankan      html  css  js  c++  java
  • Android View之布局加载流程

     

    1.引言

    最近准备重新学习下Android,加深理解,快速形成自己的知识结构体系。最先学习的就算View部分,从自定义View到Activty层次结构,到layout加载过程。等等都会看一遍,在此记录下Layout的加载过程

    2.正题

    2.1 Activity的流程加载

    Activity类中setContentView 追踪(善于用bookMark)

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

    追踪代码,PhoneWindow的setContentView方法:


     
    image.png
    mLayoutInflater.inflate(layoutResId,mContentParent)
    

    由代码片段可以看出来:layoutResId的父布局就是mContentParent。那么mContentParent究竟是什么呢?

    我们知道DecorView 包括TitleView +ContentView.


     
    image.png

    追踪代码:installDescor():

     
    image.png

    generateLayout()方法:

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        // 获得窗体的 style 样式
        TypedArray a = getWindowStyle();
     
        // 省略大量无关代码
     
        // Inflate the window decor.
        int layoutResource;
        int features = getLocalFeatures();
     
        //填充带有 style 和 feature 属性的 layoutResource (是一个layout id)
     
        View in = mLayoutInflater.inflate(layoutResource, null);
     
        // 插入的顶层布局 DecorView 中
     
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
     
        // 找到我们XML文件的父布局 contentParent 
     
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        // 省略无关代码
        mDecor.finishChanging();
        // 返回 contentParent 并赋值给成员变量 mContentParent
        return contentParent;
    }
    

    给DecorView添加一个名字为mContentRoot的view。并且mContentRoot里面包含了一个id为ID_ANDROID_CONTENT的 mContentParent.

    得到了mContentParent之后,在根据mLayoutInflater.inflate(layoutResId,mContentParent) 就将Layout布局加载进了DecorView,然后这个视图才能被我们看到。

    验证:


     
    image.png

    说明了mContentParent是FramLayout

    3.AppCompatActivity 加载View得过程

      @Override
        public void setContentView(@LayoutRes int layoutResID) {
            getDelegate().setContentView(layoutResID);
        }
    

    getDelegate():

        @NonNull
        public AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                mDelegate = AppCompatDelegate.create(this, this);
            }
            return mDelegate;
        }
    

    跟踪create()方法,

      public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
            return create(activity, activity.getWindow(), callback);
        }
    

    activity.getWindow() 由上面可知,得到的肯定是PhoneWindow。问题来了,activity这个时候真的能得到一个非null的PhoneWindow吗?。发现在activity调用attach方法的时候就会初始化PhoneWindow:

     final void attach(Context context, ActivityThread aThread,
                Instrumentation instr, IBinder token, int ident,
                Application application, Intent intent, ActivityInfo info,
                CharSequence title, Activity parent, String id,
                NonConfigurationInstances lastNonConfigurationInstances,
                Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                Window window, ActivityConfigCallback activityConfigCallback) {
            attachBaseContext(context);
    
            mFragments.attachHost(null /*parent*/);
    
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
            mWindow.setWindowControllerCallback(this);
            ......
    }
    

    Activity.attach是在performLaunchActivity中本调用,也就是在ActivityThread中被调用。所以在Activity中任何时候getWindow(),都能得到PhoneWindow。

    继续跟踪代码:
    create方法调用的是:

     private static AppCompatDelegate create(Context context, Window window,
                AppCompatCallback callback) {
            if (Build.VERSION.SDK_INT >= 24) {
                return new AppCompatDelegateImplN(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 23) {
                return new AppCompatDelegateImplV23(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 14) {
                return new AppCompatDelegateImplV14(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 11) {
                return new AppCompatDelegateImplV11(context, window, callback);
            } else {
                return new AppCompatDelegateImplV9(context, window, callback);
            }
        }
    

    setContentView 是在AppCompatDelegateImplV9类中被重写,也就是说最终是调用AppCompatDelegateImplV9.setContentView();

      @Override
        public void setContentView(int resId) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }
    
      ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    

    很容易让我们想到mSubDecor 是不是等同于上文中的mContentRoot 呢?

    看看ensureSubDecor怎么写的:

     if (mOverlayActionMode) {
                    subDecor = (ViewGroup) inflater.inflate(
                            R.layout.abc_screen_simple_overlay_action_mode, null);
                } else {
                    subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
                }
    

    subDecor 是系统提供的ViewGroup 里面有一个android.R.id.content布局,和mContentRoot 作用一模一样。

    基本上分析到此结束,也是比较简单。

     
  • 相关阅读:
    PHP观察者模式
    php减少损耗的方法之一 缓存对象
    php迭代器模式
    数据库安全措施的改进依据------未实践
    mysql利用phpmyadmin导入数据出现#1044错误 的可能原因
    两列布局的基本思路
    less1.5中的减错误
    ie63像素bug原因及解决办法不使用hack
    镜像翻转二叉树
    判断一个整数是否是 2 的幂次方
  • 原文地址:https://www.cnblogs.com/xgjblog/p/9441820.html
Copyright © 2011-2022 走看看