本文是Android视图层源码分析系列第一篇文章。主要来理清Window
的地位以及做用。java
Android中全部的视图(
View
)都是经过Window
来呈现的,无论是Activity
、Dialog
仍是Toast
,它们的视图实际上都是附加在Window
上的,所以Window
实际是View
的直接管理者。本文就从源码来分析一下Window
,理清Window
是如何组织视图(View
)以及Activity
的PhoneWindow
的工做原理。本文不会去讨论Window
的详细使用。android
分析以前,咱们先找一个切入点,如下面这段代码为例:git
WindowTestActivity.javagithub
// example 1
val simpleTv = getSimpleTextView()
windowManager.addView(simpleTv, getSimpleWindowLayoutParams())
//example 2
window.addContentView(getSimpleTextView(), ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
复制代码
即咱们直接经过windowManager.addView
和window.addContentView()
来添加了一个View
。这两个方法都是Activity
直接提供的方法,也是咱们惟一与Window
交互的几个方法之一,那:bash
View
究竟添加到了哪里呢?window
和windowManager
有什么关系呢?下面咱们将从源码一点一点弄清这些问题。先来看一下windowManager.addView(contentView, layoutParams)
,为了下面方便叙述,咱们把被add
的view
叫作contentView
。微信
WindowManager
是一个接口,在看windowManager.addView()
以前咱们先来看一下Activity的WindowManager
的实例是谁。app
追踪Activity
的源码发现WindowManager
实际上是经过Window
来获取的(它实际上是Window
的成员变量)源码分析
mWindowManager = mWindow.getWindowManager();
复制代码
那Window
的WindowManager
是在什么地方赋值的呢?实际上是在Activity attch
时:布局
//Activity.java
final void attach(...){
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken,..);
}
复制代码
mWindow.setWindowManager()
内部实际上是构造了一个WindowManagerImpl
:ui
public void setWindowManager(WindowManager wm, IBinder appToken...) {
...
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
复制代码
即 Activity
的PhoneWindow
的WindowManager
实例是WindowManagerImpl
而且在Activity.attach
方法中也能够看出Activity
的Window
的实例时PhoneWindow
(PhoneWindow
实际上是Window
的惟一实现类,是针对于app客户端(相对于Android系统)
的一个Window
实体)。
WindowManagerImpl
其实只是一个简单的装饰类,全部操做直接转发到了WindowManagerGlobal
, 所以windowManager.addView()
源码的追踪能够直接看WindowManagerGlobal.addView()
:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}
复制代码
parentWindow
这个参数其实就是Activity
的window(PhoneWindow)
。而WindowManagerGlobal.addView()
作的主要事情是:
(root)ViewRootImpl
contentView
相关对象放入到mViews/mRoots/mParams
集合中。(若是contentView
被移除,那么这3个集合相关对象也会被移除)root.setView(contentView..)
会经过IPC调用到WindowManagerService
来在window
中显示contentView
。因此windowManager.addView()
作的事情是:
为contentView
建立一个ViewRootImpl
对象,并把contentView
相关对象放入到mViews/mRoots/mParams
集合中维护起来,而后调用ViewRootImpl.setView(..)
方法来显示contentView
因此到这里能够用下面这张图总结一下Activity/Window/WindowManager
之间的关系:
通过上面的分析咱们还知道 : 经过windowManager.addView(contentView)
来显示视图实际上是和Activity
的Window
有着密切的联系的(显示一个视图必需要有Window
)。那Activity
的视图是怎么显示的呢?
咱们继续看一下(其实Activity
的视图也是经过windowManager.addView(contentView)
的方式来显示的):
追踪Activity.setContentView(..)
源码能够看到:
getWindow().setContentView(contentView);
复制代码
即咱们的contentView
实际上是设置给了Window(PhoneWindow)
:
PhoneWindow.java
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
mContentParent.addView(view, params);
}
复制代码
即咱们Activity
的根布局View实际上是添加到了PhoneWindow的mContentParent成员变量中
中。那mContentParent
是什么呢?看一下PhoneWindow.installDecor()
,这个方法也很长,所以只截取最重要的部分看一下:
private void installDecor() {
mDecor = generateDecor(-1); // decor 的实例时DecorView,它继承自FrameLayout
...
mDecor.setWindow(this); //DecorView 绑定一个window
...
mContentParent = generateLayout(mDecor); // mContentParent 会被add到 decor view中
...
}
复制代码
根据上面的注释,咱们能够先这样理解PhoneWindow/DecorView/mContentParent
的关系:
PhoneWindow
里存在一个DecorView(mDecor)
成员变量,能够把它理解为一个FrameLayout
,它包含一个mContentParent
的子View, mContentParent
是Activity
的根布局contentView
的父View
那mContentParent
是一个什么样的布局/View
呢?继续看一下generateLayout(mDecor)
:
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource; //mContentParent的布局文件
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
....各类 if else
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
}
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //会把这个布局文件inflate出的view,添加到DecorView中
//经过 findViewById 来获取 ContentParent。 这个id其实就来自 layoutResource 所指向的布局文件
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}
复制代码
上面我作了一些注释,能够理解为mContentParent
就是DecorView
的子View。layoutResource
根据当前Activity
的Theme
的设置,会对应到许多不一样的布局文件,R.layout.screen_toolbar
是给Activity
设置默认 Theme
是所对应的布局文件:
<com.android.internal.widget.ActionBarOverlayLayout
android:id="@+id/decor_content_parent"
...>
<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:gravity="top">
<Toolbar
android:id="@+id/action_bar"
... />
<com.android.internal.widget.ActionBarContextView
android:id="@+id/action_context_bar"
..../>
</com.android.internal.widget.ActionBarContainer>
</com.android.internal.widget.ActionBarOverlayLayout>
复制代码
看一个具体的Android Layout Inspectot
分析:
ContentFrameLayout
是support v7
的类,能够把它理解为FrameLayout
。
PhoneWindow
的视图层级能够用下图表示
通过上面的分析: PhoneWindow
的视图层级其实就是DecorView
的视图层级。DecorView
就是Activity
视图的根View
。
因此Activity
的视图显示的过程其实就是DecorView
的视图显示的过程。那DecorView
如何显示呢?
它的显示原理也是使用windowManager.addView()
:
在Activity Resume
时,DecorView
会添加到WindowManager
中:
ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
final Activity a = r.activity;
...
wm.addView(decor, l);
}
复制代码
即在Activity Resume
时DecorView
添加到WindowManager
中,进而经过WindowManagerService
来显示成功。因此Activity.onResume()
用户才能够看到Activity
的视图。
总结一下到目前为止所分析的点:
Window
。WindowManager
属于Window
,负责管理Window
中View
的显示。在Window
中显示View
咱们应使用它的接口Window
能够有多个子View,每一个子View
都对应一个ViewRootImpl
。ViewRootImpl
会经过IPC来与WindowManagerService
交互,来实现View
的显示它们之间的关系以下图:
下一篇文章将继续分析ViewRootImpl.setView(..)
所引发的WindowManagerService
的操做,即Window
是怎么在屏幕上展现内容的。
欢迎关注个人Android进阶计划看更多干货
欢迎关注个人微信公众号:susion随心
参考文章: