深入了解Window
本文是Android视图层源码分析系列第一篇文章。主要来理清Window的地位以及作用。
Android中所有的视图(
View)都是通过Window来呈现的,不论是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因而Window实际是View的直接管理者。本文就从源码来分析一下Window,理清Window是如何组织视图(View)以及Activity的PhoneWindow的工作原理。本文不会去探讨Window的详细使用。
分析之前,我们先找一个切入点,以下面这段代码为例:
WindowTestActivity.java
// example 1val simpleTv = getSimpleTextView()windowManager.addView(simpleTv, getSimpleWindowLayoutParams()) //example 2window.addContentView(getSimpleTextView(), ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))即我们直接通过windowManager.addView和window.addContentView()来增加了一个View。这两个方法都是Activity直接提供的方法,也是我们唯一与Window交互的几个方法之一,那:
- 这两个方法有什么关系与不同呢?
View到底增加到了哪里呢?window和windowManager有什么关系呢?
下面我们将从源码一点一点弄清这些问题。先来看一下windowManager.addView(contentView, layoutParams),为了下面方便叙述,我们把被add的view叫做contentView。
通过WindowManager增加一个View
WindowManager实例的创立
WindowManager是一个接口,在看windowManager.addView()之前我们先来看一下Activity的WindowManager的实例是谁。
追踪Activity的源码发现WindowManager其实是通过Window来获取的(它其实是Window的成员变量)
mWindowManager = mWindow.getWindowManager();那Window的WindowManager是在什么地方赋值的呢?其实是在Activity attch时:
//Activity.javafinal void attach(...){ mWindow = new PhoneWindow(this, window, activityConfigCallback); ... mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken,..);}mWindow.setWindowManager()内部其实是构造了一个WindowManagerImpl:
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之间的关系:
Activity_Window_WindowManager.png
经过上面的分析我们还知道 : 通过windowManager.addView(contentView)来显示视图其实是和Activity的Window有着密切的联络的(显示一个视图必需要有Window)。那Activity的视图是怎样显示的呢?
我们继续看一下(其实Activity的视图也是通过windowManager.addView(contentView)的方式来显示的):
Activity的视图的显示
追踪Activity.setContentView(..)源码可以看到:
getWindow().setContentView(contentView);即我们的contentView其实是设置给了Window(PhoneWindow):
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分析:
LayoutInspector.png
ContentFrameLayout是support v7的类,可以把它了解为FrameLayout。
PhoneWindow的视图层级可以用下图表示
PhoneWindow的视图层级.png
经过上面的分析: PhoneWindow的视图层级其实就是DecorView的视图层级。DecorView就是Activity视图的根View。
所以Activity的视图显示的过程其实就是DecorView的视图显示的过程。那DecorView如何显示呢?
它的显示原理也是使用windowManager.addView() :
DecorView的显示
在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的视图。
总结
总结一下到目前为止所分析的点:
- 视图(View)的显示离不开
Window。 WindowManager属于Window,负责管理Window中View的显示。在Window中显示View我们应使用它的接口- 一个
Window可以有多个子View,每个子View都对应一个ViewRootImpl。 ViewRootImpl会通过IPC来与WindowManagerService交互,来实现View的显示
它们之间的关系如下图:
Window的功能分析.png
下一篇文章将继续分析ViewRootImpl.setView(..)所引起的WindowManagerService的操作,即Window是怎样在屏幕上展现内容的。
欢迎关注我的Android进阶计划看更多干货
欢迎关注我的微信公众号:susion随心
微信公众号.jpeg
参考文章:
- 《Android开发艺术探究》
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 深入了解Window