Activity的启动
启动Activity其实就是给Activity创建实例,分配任务栈,执行生命周期回调。Activity是如何被显示在屏幕上的,作为应用层的开发者我们其实只做了一件事情,就是在onCreate生命周期调用Activity的setContentView。剩下的是framework层以下的代码和硬件共同来完成的了。
scss
setContentView(R.layout.activity_main);
熟悉Activity生命周期的肯定都知道,执行到onResume生命周期的时候,Activity才是可见的。所以要弄清楚Activity的显示原理。需要弄懂两个问题
第一,setContentView方法都做了哪些事情?
第二,到onResume()这个生命周期里,Activity做了哪些事情?
Activity.setContentView做了什么
1、初始化mWindow
在Activity.java中,setContentView方法会先调用getWindow获取一个window类型的实例,然后再继续调用window的setContentView方法。获取到的window实例其实是Activity中的一个叫做mWindow的属性。在attach方法中可以看到mWindow其实是一个PhoneWindow类型的实例。
scss
private Window mWindow;
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, ......) {
......
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
}
2、调用mWindow.setContentView
接着看PhoneWindow的setContentView方法,mContentParent如果为空做了两件事installDecor和inflate,如果不为空,那么将移除所以子View进行复用。
ini
ViewGroup mContentParent;
private DecorView mDecor;
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
......
//将contentView加载到DecorVoew当中
mLayoutInflater.inflate(layoutResID, mContentParent);
......
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//实例化DecorView
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(mDecor);
......
}
}
//得到mContentParent
protected ViewGroup generateLayout (DecorView decor){
......
int layoutResource;
if (features ...) {// 根据feature选择系统布局
layoutResource = ...
}
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//找到contentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
在mContentParent为空的时候,会调用installDecor方法。
installDecor中当mDecor为空的时候,调用generateDecor来创建一个mDecor。DecorView其实是Framelayout的一个子类。
再往下当mContentParent为空的时候,就会调用generateLayout的方法,赋值mContentParent。在generateLayout方法中,首先会根据features选择一种系统布局样式。接着调用onResourcesLoaded方法,根据系统布局的ID把它转化成View,依然是通过inflated的方式。再通过addView把它添加为DecorView的子View。
java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
final View root = inflater.inflate(layoutResource, null);
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
generateLayout里面的在最后会把contentParent返回。因为contentParent添加了我们自己实现的布局,也就是contentView。所以可以理解为contentParent其实是系统布局中留给contentView的位置。那么DecorView就是系统布局的父view,这个DecorView是整个Activity的ViewTree显示的RootView。Activity的setContentView中的UI是DecorView中的一部分ContentView。
Activity、PhoneWindow、DecorView、ContentView的关系
一个 Activity 对应一个 Window 也就是 PhoneWindow,一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout。DecorView 中的一个子视图ContentView,用于显示我们写的xml布局。
- Activity:代表应用程序中单个屏幕上的用户界面。
- PhoneWindow:是 Activity 的一个子类,用于管理 Activity 的窗口和界面。每个 Activity 都有一个与之关联的 PhoneWindow 对象,用于控制窗口的外观、布局和交互行为。
- DecorView:是 PhoneWindow 中的一个重要组件,它是窗口的根布局视图。DecorView 包含了整个窗口的内容,包括状态栏、标题栏、导航栏和应用程序自定义的界面元素。
- ContentView:是 DecorView 中的一个子视图,用于承载应用程序的主要内容界面。
总结
总结一下setContentView的作用。
第一个就是创建DecorView,这是个FrameLayout;
第二个就是找到ContentParent,这是个ViewGroup。
第三个就是把我们自己实现的布局转换成ViewTree,并且挂在ContentParent的下面,形成一个完整的activity的ViewTree。