View树与Window关联
前面介绍的是View树的创建,但是创建好的View树只有添加到一个Window里才会显示到屏幕上,因为
WindowManagerService
服务会为每一个Window创建一块Surface作为画布,View树里所有的View都会绘画到这块Surface上,最后
SurfaceFlinger
服务会将所有Window创建的的Surface传递给GPU,最终显示到屏幕上。
创建Window
通过
WindowManager.addView
接口就可以申请创建一个新的Window并添加一个View树,弹出菜单、浮动窗口等自定义的窗口都是通过这个接口显示出来的:
//获得WindowManager服务
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//设置Window参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.setTitle("FloatWindow");
//创建View树
View rootView = getLayoutInflater().inflate(R.layout.float_layout, null);
//创建Window并关联View树
windowManager.addView(rootView, params);
应用中使用的
WindowManager
其实是一个
WindowManagerImpl
类的实例:
frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
...
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
如上面的代码,
WindowManagerImpl.addView()
实现会调用
WindowManagerGlobal.addView()
,实际的请求创建Window的逻辑也在这个类里实现:
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public final class WindowManagerGlobal {
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
...
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(params);
try {
root.setView(view, params, panelParentView);
} catch (RuntimeException e) {
throw e;
}
}
}
}
WindowManagerGlobal
类里有这三个
ArrayList
列表:
-
保存着每个Window的根ViewmViews
-
保存每个根View对应的mRoots
ViewRootImpl
-
保存的是每个Window的窗口配置参数mParams
WindowManager.LayoutParams
WindowManagerGlobal.addView()
的实现里会为每一个Window创建一个
ViewRootImpl
实例,以及窗口配置参数
WindowManager.LayoutParams
实例,并将传递过来的根View实例保存在上面提到的三个列表里。
addView()
实现里最后调用的
root.setView()
就是通过
ViewRootImpl
将Window与View树关联起来,同时
ViewRootImpl
还有控制View树的刷新、显示以及输入事件分发的作用。
创建Activity主界面
Android 系统中的Activity组件也是通过View树与Window显示出来的,只是将创建View树与Window的逻辑封装到了生命周期的处理流程里了。
所有Activity的窗口都是用一个
PhoneWindow
类的实例表示的,每个
PhoneWindow
都会创建一个内部类
DecorView
作为这个窗口中View树的根View。下面的结构图表示的就是
Activity
、
PhoneWindow
以及
DecorView
之间的关系,下面我们通过这个结构图结合相关代码来研究一下Activity主界面的View树的创建过程。
- 应用里Activity一般会在
里通过调用onCreate
设置自己主界面的View树布局,如下面的代码:setContentView()
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
setContentView()
有多个重载实现,既可以传递布局资源ID也可以直接传递创建好的View。
Activity
里会将
setContentView()
设置过来的View传递给
PhoneWindow
:
frameworks/base/core/java/android/app/Activity.java
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()返回的就是PhoneWindow的实例。
- PhoneWindow里的setContentView实现如下面的代码:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
private DecorView mDecor;
private ViewGroup mContentParent;
public void setContentView(int layoutResID) {
...
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
mContentParent
是用作为
ContentView
的父View的,
setContentView()
设置过来的Layout 资源ID通过
LayoutInflater
解析后会被添加到
mContentParent
的子View树里
- 如果这个Activity是第一次启动,那么
应该是mContentParent
的,需要在null
里初始化所有的装饰View:installDecor()
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
-
的作用是创建出generateDecor()
的实例,如下面:DecorView
protected DecorView generateDecor() {
...
return new DecorView(getContext(), -);
}
-
的作用是根据Activity设置的各种feature、Theme创建generateLayout()
、ContentParent
等Activity的基础视图框架,如下面:ActionBar
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
...
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
...
mDecor.finishChanging();
return contentParent;
}
如果
Activity
设置了需要
ActionBar
,那么就会加载有
ActionBar
的布局资源:
if ((features & ( << FEATURE_ACTION_BAR)) != ) {
layoutResource = R.layout.screen_action_bar;
} else {
layoutResource = R.layout.screen_title;
}
有
ActionBar
的界面会使用
R.layout.screen_action_bar
布局资源,没有
ActionBar
会使用
R.layout.screen_title
(5.0引入Material主题后有新引入了一个
R.layout.screen_toolbar
):
frameworks/base/core/res/res/layout/screen_action_bar.xml
<com.android.internal.widget.ActionBarOverlayLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/decor_content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
android:theme="?attr/actionBarTheme">
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.internal.widget.ActionBarContainer
android:id="@+id/action_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
android:touchscreenBlocksFocus="true"
android:gravity="top">
......
</com.android.internal.widget.ActionBarContainer>
......
</com.android.internal.widget.ActionBarOverlayLayout>
contentParent
会取其中ID为
R.id.content
的View,应用的Activity里设置的ContentView会被添加到
contentParent
的子View里
上面是Activity的View树的创建过程,而Activity的Window需要等到Activity resume的时候才会创建,同样也是调用
WindowManager.addView
接口,而根View就是前面创建的
DecorView
:
frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
...
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
...
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
}
}