Android-setContentView()

结构图

  • 一个Activity对应一个window,window是在activity.attach()函数里面被创建的。
  • activity.attach() 在AMS创建Activity的时候调用的。
  • 在Activity的onCreate()函数里面调用setContentView()最后调用的是window的setContentView()。
  • 调用window.setContentView()函数,里面初始化了 DecorView,以及内部的TitleView和mConentParent,mConentParent是DecorView的子View。
  • 我们添加进去的R.layout.activity_main.xml文件最后转化成View,添加到mConentParent里面。
  • 这里的mConentParent 就是上图去掉TitleView的ContentView。

XML是如何显示出来的

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 将创建好的xml文件 传入 系统就帮我们显示出来了
        setContentView(R.layout.activity_main);
    }
点击进入Activity的setContentView();
 public void setContentView(@LayoutRes int layoutResID) {
        // 调用了一window的方法
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

 public Window getWindow() {
        return mWindow;
    }
这个mWindow什么时候创建的呢? Activity#attach()
@UnsupportedAppUsage
    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, IBinder assistToken) {
        attachBaseContext(context);

       ....

        // 在这里创建了 Window对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
       
       ....
        
        // 这个时候也创建了windowmanager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       ....
       
    }
  • ActivityThread.performLaunchActivity()调用 activity.attach()
下面看一下 PhoneWindow.setContentView()
    @Override
    public void setContentView(View view) {
        // 先判断mContentParent是否初始化
        if (mContentParent == null) {
            // 划重点 --> 初始化Decor  
            // 当前activity第一次调用setContentView()
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            // 如果Activity没有过渡动画,多次调用setContentView()回走到这里
            // 移除mContentParent所有内部view
            mContentParent.removeAllViews();
        }

        // 判断是否使用了Activity的过度动画
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            // 设置动画场景
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());
            transitionTo(newScene);
        } else {
            // 将资源文件通过LayoutInflater对象封装为View树
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            // 这个方法在Activity是一个空实现
            // 说明在Activity的布局改动时(setContentView或者addContentView 方法执行完毕后会回调该函数)
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
installDecor(); 干了啥
private void installDecor(){
    if(mDecor==null){
        // 如果mDecor为空则创建一个DecorView实例
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }

        if (mContentParent == null) {
            // 如果mContentParent为空则通过generateLayout创建一个
            mContentParent = generateLayout(mDecor);
            ...
        }    

    }
}
  • 创建了DecorView和mContentParent
generateDecor(-1);
 protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        // 返回一个实例
        return new DecorView(context, featureId, this, getAttributes());
    }
到这里我们setContentView(R.layout.activity_main); xml文件已经添加到DecorView了。

每个Activity都有一个与之关联的window,那如何管理window呢?

Activity的创建 ActivityThread
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {
       
        ....

        final Activity a = performLaunchActivity(r, customIntent);

        ....

        return a;
    }
performLaunchActivity() 在这里创建了一个Activity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ....
       Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
     ....
}
DecorView和WindowManager是如何关联起来的?
  • activityThread.handleLaunchActivity()
@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        
        ....
        // 这里会调用activity.onResume()函数 
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ...
        final Activity a = r.activity;
        ...

        if (r.window == null && !a.mFinished && willBeVisible) {
            // 获取Window对象
            r.window = r.activity.getWindow();
            // 获去Window中的DecorView对象
            View decor = r.window.getDecorView();
            // 在这里被放开了 
            decor.setVisibility(View.INVISIBLE);
            // 获取WindowManager对象
            // 这个WindowManager就是在activity.attach()的时候一起创建的
            ViewManager wm = a.getWindowManager();

            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 看这里  将DecorView 添加到 wm 里面去了 
                    // TODO 这就是为什么 activity.onResume()之后才有界面
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
            ...
            if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    // 设置decorview可见
                    r.activity.makeVisible();
                }
           }

        } 
    }

ActivityThread.handleLaunchActivity()--> 去创建Activity
ActivityThread.performLaunchActivity()--> 去创建Activity
Activity.attach()--> activity创建的时候调用
Activity.onCreate()--> 生命周期函数
ActivityThread.handleResumeActivity()--> 调用下面的函数
ActivityThread.performResumeActivity()--> 返回ActivityClientRecord
Activity.onResume()--> 生命周期函数
windowManager.addView()

到这里 Activity的创建 DecorView的创建 WindowManager的创建以及DecorView添加到WindowManager,这也是为什么在onCreate()里面是获取不到View的宽高的。
ViewManager
public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
WindowManager
public interface WindowManager extends ViewManager {
      ....
}
WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {

...

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInatance();
private final Context mContext;
private final Window mParentWindow;


public WindowManagerImpl(Context context) {
    this(context, null);
}

private WindowManagerImpl(Context context, Window parentWindow) {
    mContext = context;
    mParentWindow = parentWindow;
}


public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
    return new WindowManagerImpl(displayContext, mParentWindow);
}

// 往Window中添加View
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

// 更新布局 
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}

// 将删除View的消息发送到MessageQueue中,稍厚删除
@Override
public void removeView(View view) {
    mGlobal.removeView(view, false);
}
   
// 立即删除window中的View 
@Override
public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
}

...

}
  • WindowManagerImpl并没有直接实现操作View的相关方法,而是全部交给WindowManangerGlobal。WindowManagerGlobal 是一个单例类----即一个进程中最多仅有一个。创建WindowManagerGlobal对象的方式如下
public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

private WindowManagerGlobal() {
}

深入分析WindowManagerGlobal(分析内部的三个方法)

  • addView(View view,ViewGroup.LayoutParams params)
    该方法的主要作用是将decorView添加到viewRootImpl中,通过viewRootImpl 对其内部进行管理
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        // 参数检查
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        // 判断是否有父Window,从而调整当前窗口的布局参数
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // 有,调整title和token
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // 无,应用开启了硬件加速,decorView就开启 
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            if (mSystemPropertyUpdater == null) {
                // 创建一个Runnable,用来遍历更新所有ViewRootImpl,更新系统参数
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                // 添加到执行队列
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            // 创建一个ViewRootImpl对象并保存在root变量中
            root = new ViewRootImpl(view.getContext(), display);
            // 给需要添加的View设置params,也就是decorview
            view.setLayoutParams(wparams);
            // 将decorview、布局参数以及新建的ViewRootImpl保存在三个集合中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            try {
                // 将decorview设置给ViewRootImpl
                // ViewRootImpl向WMS添加新的窗口、申请Surface以及decorView在Surface的重绘动作
                // 这才是真正意义上完成了窗口添加操作
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

updateViewLayout(View view,ViewGroup.LayoutParams params)
removeView(View view)

通过ViewRootImpl管理View

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271