从纯sdk及framwork的角度看,android中界面框架相关的类型有:Window,WindowManager,View等。下面就以这几个类为出发点来概览下安卓开发的“界面架构”。

Window

该类接触不多,和它密切相关的View类就比较熟悉了。

Window和View的关系

View是可视界面上的一个矩形区域,它显示内容并接收各种交互事件。所有View形成一个ViewTree这样的结构,对应任何一个界面通过sdk自带的hierarchyviewer工具就可以看到所有View对象形成的视图树的形象的结构图,相信都不会陌生。

一般的,开发工作主要是利用系统及自定义控件组合完成各种界面,所以理解View的使用和原理更重要些。再进一步,以ViewTree为整体,再看它和window,系统服务之间的关系可以从整体上把握android中界面框架。

Window类的描述如下:

Window: Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

Window表示“窗口”的概念,类似桌面OS中的窗口的概念,它是对用户界面的一个逻辑划分。可以参考下Windows编程中对Window类的描述是:“The point of interaction between a user and a standalone application is a window.”。每个窗口对应一个独立的交互(可以是完整屏幕大小的)界面。

可以认为窗口是系统区分不同界面(不同app,或者同一app的不同Activity等)的一个单位。窗口之间可以包含(容器和子窗口),可以重叠(窗口具有z轴深度)。

窗口本身没有显示内容的能力,它包含一个顶级的View对象来持有一棵ViewTree。

一句话概况:窗口是一个独立的可交互界面,不同窗口叠加显示,窗口包含View来显示内容。

android中的UI就是View组成的ViewTree来表达的,root view或者说顶部(top level)的View对象作为对整个ViewTree执行消息传递,测量,布局和绘制等遍历操作的全局入口,持有此root view就相当于持有对应的组成界面内容的ViewTree。

有一点就是,Window是一个框架层的概念,整个android中的“各种界面”是不同类型的Window对象。但是在应用层,我们创建不同的界面就是提供不同的“内容”View对象,然后指定其Window类型,而界面的创建,更新和关闭是通过操纵Window所包含的要显示的顶级View,而不会直接去操控Window对象。

创建Window

在一个新窗口显示一个View最简单的过程如下:

private void openNewWindow() {
Button button = new Button(this);
button.setText("Button On New Window");
WindowManager.LayoutParams params
= new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = 220;
params.y = 320;
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(button, params);
}

创建Window是通过WindowManager实现的,addView(View view, WindowManager.LayoutParams params)方法接收要展示的root view和窗口布局相关的参数。

执行上面的代码会创建一个新的窗口,并在屏幕坐标(220,320)的位置放置了一个Button。应用层要做的就是准备好Button对象,然后设置好相关布局参数,而Window对象的创建本身最终是通过系统服务完成的。类似Activity那样,Window对象的创建不是new出来的。

窗口的创建,更新和关闭操作都是WindowManager的工作。实际开发中很少和Window直接打交到。比如Activity对应一个窗口,但是对它的界面的各种操作好像都和Window无关,因为Window的确够“底层”了。

窗口类型

前面说过,安卓中的界面划分为一个个窗口,系统运行中各个不同的窗口可以叠加显示。和叠加相关的属性就是Z-ordered,它是正整数。通过Z-ordered值,系统来决定这些Window的覆盖顺序。但实际上并不是通过指定Z-ordered值来直接控制窗口的层叠,而是,系统提供了一组常量,被表示为窗口类型,不同窗口类型的常量对应一个Z-ordered的值范围,然后其它地方通过为Window指定type来间接控制其Z-ordered。Window类有一个setType的方法正是做这件事情的。不过在创建Window时就需要为其指定type,而我们不直接接触Window对象,WindowWindowManager.LayoutParam有一个type的变量,在通过WindowWindowManager.addView方法创建窗口时,窗口布局参数中指定需要的type——窗口类型——这是必须的,而且Window type can not be changed after the window is added。

窗口类型有:

  • 系统窗口:如状态栏,Toast那样的,常量如TYPE_SYSTEM_xx的。
  • 应用窗口:就是Activtiy。
  • 子窗口:Dialog这样的,需要依附(attach)到其它窗口(作为子窗口的container,如Activity)。

WindowManager.LayoutParams有个flags的属性,用来控制窗口的可见性,透明,是否可以获得焦点等显示和交互的有关状态。

Window相关属性和方法

接下来直面Window类。它是一个抽象类,目前只有PhoneWindow是其实现类。可以通过下面截取的类型定义的代码片段对Window有个感官认识:

public abstract class Window {
// 一系列的FEATURE_xx常量,还记得在Activity中requestWindowFeature方法吗?
public static final int FEATURE_xxx /**
* API from a Window back to its caller.
* This allows the client to intercept key
* dispatching, panels and menus, etc.
*/
private Callback mCallback;
// The interface that apps use to talk to the window manager.
private WindowManager mWindowManager; private Window mContainer;
private Window mActiveChild;
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams(); /**
* The ID that the main layout in the XML layout file should have.
*/
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; /**
* API from a Window back to its caller. This allows the client to
* intercept key dispatching, panels and menus, etc.
*/
public interface Callback {
public boolean dispatchTouchEvent(MotionEvent event);
public void onAttachedToWindow();
// ...
}
public abstract LayoutInflater getLayoutInflater();
public abstract void setContentView(View view, ViewGroup.LayoutParams params); /**
* Retrieve the top-level window decor view (containing the standard
* window frame/decorations and the client's content inside of that), which
* can be added as a window to the window manager.
*
* <p><em>Note that calling this function for the first time "locks in"
* various window characteristics as described in
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
*
* @return Returns the top-level window decor view.
*/
public abstract View getDecorView(); public abstract void setTitle(CharSequence title);
public void setIcon(@DrawableRes int resId) { }
}

有三个值得关注的:

  • Window.Callback

    该接口使得依靠(可以确定的是所有界面都是通过Window展示的)Window来显示内容的其它对象——主要就是Activity——接收Window发送的交互等事件和消息回调。例如Activity.onAttachedToWindow()这样的回调。

  • getDecorView

    DecorView就是和Window关联的用来显示内容的ViewTree的root。所有地方都使用Window来显示内容,不同的Window使用者会需要不同的DecorView,比如Activity就需要DecorView本身具备ActionBar这类的“界面装饰部分”。而Dialog明显没有。

  • setContentView

    Window显示的自定义内容。Activity中的setContentView正是调用关联的Window对象的此方法。将界面内容附加到DecorView作为其子树。

PhoneWindow

PhoneWindow作为目前Window的唯一子类,它的大致定义如下:

public class PhoneWindow extends Window {
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor; // This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent; private TextView mTitleView;
private ImageView mLeftIconView;
private ActionBarView mActionBar; @Override
public void injectInputEvent(InputEvent event) {
getViewRootImpl().dispatchInputEvent(event);
} private ViewRootImpl getViewRootImpl() {
if (mDecor != null) {
ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
if (viewRootImpl != null) {
return viewRootImpl;
}
}
throw new IllegalStateException("view not added");
} private final class DecorView extends FrameLayout {
// ...
}
}
  • DecorView

    PhoneWindow的内部类DecorView作为Window关联的ViewTree的root view。它用来放置ActionBarView,Title和Icon等一些在Activity中经常访问得到的界面元素。而应用自身提供的contentView是作为DecorView的childView存在的。

  • ViewRootImpl

    从View组成ViewTree的角度看,任意的ViewGroup子类都可以做为root,但Window操作ViewTree时需要对root view做特殊对待,ViewRootImpl正是用来表达root view,是Window和ViewTree之间的交互接口:

/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link WindowManagerGlobal}.
*
* {@hide}
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent {
// ...
}

在View中方法ViewRootImpl getViewRootImpl()可以获得一个View关联的ViewRootImpl对象。在ViewTree关联到Window后,每个View会获得一个AttachInfo对象,里面保存了rootView和ViewRootImpl这样的对象来访问根视图。

至于ViewRootImpl到底有哪些作用,后面分析WindowManager的操作时会接触到。目前为止,Window和View的大致概念和它们之间的关系交代完毕。下面需要从WindowManager来继续探索界面框架的工作原理。

WindowManager

概念window manager表示用来管理操作Window的角色。稍后会知道真实的动作完全是通过IPC调用系统服务WindowManagerService完成的,我们(调用api的客户端代码)接触到的是一个代理对象。而WindowManager正是IPC的接口描述。

接口WindowManager继承了ViewManager,它的定义是:

/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager {
// Assign the passed LayoutParams to the passed View and add the view to the window.
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}

上面的三个方法是面向Window的,分别用来添加,更新、和移除View。

Window是界面框架的基本单位,每个可视的独立交互的界面是一个Window,而界面可视元素是View组成的,View必须依附到Window来被最终绘制和显示。SDK提供的界面又分为不同的Window类型,Dialog,StatusBar,Toast,Activity等,它们都是拥有View并依靠Window来显示内容的“界面类”。

ViewManager的这三个方法几乎就是WindowManager的所有职责,也是我们可以“间接”和Window打交道的方式。addView时会创建新的Window并将传递的View作为其呈现的内容。removeView时也就销毁了Window。

接口WindowManager继承ViewManager:

public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams
implements Parcelable {
// View的left,top
public int x, y;
/**
* The general type of window. There are three main classes of
* window types:Application windows、System windows、Sub-windows。
*/
public int type; // Various behavioral options/flags.
public int flags; // ...
}
}

WindowManager.LayoutParams就是和Window中View相关的布局参数。

WindowManagerService

就像Activity的创建一样,Window的创建不是像View那样new出来的。而是通过IPC调用系统服务WindowManagerService完成的。如果接触过RemoteViews的话很容易明白类似的设计。

WindowManager的实现类是WindowManagerImpl:

/**
* Provides low-level communication with the system window manager for
* operations that are bound to a particular context, display or parent window.
* Instances of this object are sensitive to the compatibility info associated
* with the running application.
*
* This object implements the {@link ViewManager} interface,
* allowing you to add any View subclass as a top-level window on the screen.
* Additional window manager specific layout parameters are defined for
* control over how windows are displayed. It also implements the {@link WindowManager}
* interface, allowing you to control the displays attached to the device.
*
* <p>Applications will not normally use WindowManager directly, instead relying
* on the higher-level facilities in {@link android.app.Activity} and
* {@link android.app.Dialog}.
*
* <p>Even for low-level window manager access, it is almost never correct to use
* this class. For example, {@link android.app.Activity#getWindowManager}
* provides a window manager for adding windows that are associated with that
* activity -- the window manager will not normally allow you to add arbitrary
* windows that are not associated with an activity.
*
* @see WindowManager
* @see WindowManagerGlobal
* @hide
*/
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
} @Override
public void removeView(View view) {
mGlobal.removeView(view, false);
} // ...
}

可以看到,WindowManagerImpl将实际操作委托给WindowManagerGlobal mGlobal完成。WindowManagerGlobal是单例的。

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) {
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");
} final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} ViewRootImpl root;
View panelParentView = null; synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
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);
}
}
} root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
} // do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
} // ...
}

mViews、mRoots、mParams几个参数保存了所有管理的Window的相关状态。WindowManager是管理所有Window的。

上面ViewRootImpl就是在addView时被创建的。

最后ViewRootImpl.setView方法。

// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// ...
requestLayout();
// ...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
// ...
} @Override
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}

方法scheduleTraversals()是对ViewTree的遍历入口。最终的WindowSession.addToDisplay完成IPC调用来创建Window用来显示界面。

此间涉及到IWindowSession,IWindow.Stub 等都是典型的aidl相关的技术了。

分析到这里,理解了Window、WindowManager和View之间的工作关系后就在全局上把握了界面框架。

简单地说,所有需要“界面”的地方,都需要通过一个Window。而具体过程,就是使用WindowManager来构造,提供需要显示地View,并设置好布局参数。

Dialog、PopupWindow,Activity这些经常用来搭建界面的类型,都是封装了Window。

比如,Activity的以下相关方法,都是和Window密切相关的:

void onAttachedToWindow();
void setContentView(int layoutResID);
Window getWindow();
WindowManager getWindowManager();
void onDetachedFromWindow();

无论如何,了解了Window、WindowManager等概念,对这些high-level的UI类型的工作原理就更加深入了。

总结

  • Window是独立交互的界面单位。android中所有界面都是不同类型的Window。
  • View组成ViewTree来表达显示内容。
  • ViewTree的每个View都指向rootView和ViewRootImpl。
  • ViewTree依附Window,Window持有ViewTree及ViewRootImpl。Window传递事件给ViewTree,对ViewTree

    执行遍历操作,完成测量、布局、绘制显示。
  • WindowManager负责调用系统服务完成Window的管理操作。
  • Window是系统服务管理的界面对象,它是系统分发界面交互事件、完成界面显示相关操作的接口。
  • Window和View是界面框架的不同分级,系统级和UI元素,使得界面框架的设计更为清晰。
  • Window是界面框架较底层的实现,更高级别上,都是使用Activity、Dialog等这些封装了基于Window的高级UI类型来完成界面构建的。

好像很简单的几个概念吧,拖拖拉拉说了一大堆~~

参考资料

(date:2016/11/3,本文使用Atom编写)

[虾扯蛋] android界面框架-Window的更多相关文章

  1. 虾扯蛋:Android View动画 Animation不完全解析

    本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析.以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程.如何被周期性调用为目标粗略分析下相关方法的执行细节 ...

  2. 【Windows编程】系列第十一篇:多文档界面框架

    前面我们所举的例子中都是单文档界面框架,也就是说这个窗口里面的客户区就是一个文档界面,可以编写程序在里面输入或者绘制文本和图形输出,但是不能有出现多个文档的情况.比如下面的UltraEdit就是一个典 ...

  3. Android Studio配置Android Annotations框架详解--说说那些坑

    我们开发过程中都需要写些findViewByid.serOnclickListener等类似的代码,虽然不费事,但是一个项目下来,工作量还是很大的.为了节省工作量,运生了很多对应的注解框架.网上的博客 ...

  4. 腾讯开源的Android UI框架——QMUI Android

    各位同学,早上好,我是你们的老朋友D_clock爱吃葱花,前些天忙着发版本,最近也在看各种各样的新知识,有好多东西想写啊啊啊啊啊.嗯,先冷静捋一下,卖个关子.扯回正题,今天继续为大家推荐一个Githu ...

  5. android系统启动框架、Activity界面显示过程详解

    一.Android系统框架 android的系统架构和其操作系统一样,采用了分层的架构.从架构图看,android分为四个层,从高层到低层分别是应用程序层.应用程序框架层.系统运行库层和linux核心 ...

  6. 【源码分享】WPF漂亮界面框架实现原理分析及源码分享

    1 源码下载 2 OSGi.NET插件应用架构概述 3 漂亮界面框架原理概述 4 漂亮界面框架实现  4.1 主程序  4.2 主程序与插件的通讯   4.2.1 主程序获取插件注册的服务   4.2 ...

  7. 2013 Visual Studio Magazine读者选择奖界面框架类获奖情况

    2013 Visual Studio Magazine读者选择奖已经正式揭晓了!据了解,截至今年此奖项已经评选了21次,非常值得.NET开发人员信赖和参考.此次评选共有400多个产品角逐28个分类的奖 ...

  8. Android 开源框架Universal-Image-Loader完全解析(三)---源代码解读

    转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/39057201),请尊重他人的辛勤劳动成果,谢谢! 本篇文章 ...

  9. 使用HTML来生产Android界面

    使用HTML来生产Android界面 (2013-03-11 17:50:39) 转载▼   分类: Android 1. HTML 开发软件界面   因为android软件开发分工目前还没有细化,程 ...

随机推荐

  1. 菜鸟学Struts2——Interceptors

    昨天学习Struts2的Convention plugin,今天利用Convention plugin进行Interceptor学习,虽然是使用Convention plugin进行零配置开发,这只是 ...

  2. 【组织级项目管理】P2 MSP P3O

    组织级项目管理--有你,有我,有大家 在过去的2年,无论对于企业来讲,还是对于我们个人都有很多大脑的冲击,有几个词大家应该特别耳熟能详:转型,变革,敏捷,互联网+,组织的项目化管理等.就是这些让我们的 ...

  3. WinForm 天猫2013双11自动抢红包【源码下载】

    1. 正确获取红包流程 2. 软件介绍 2.1 效果图: 2.2 功能介绍 2.2.1 账号登录 页面开始时,会载入这个网站:https://login.taobao.com/member/login ...

  4. 步入angularjs directive(指令)--准备工作熟悉hasOwnProperty

    在讲解directive之前,先做一下准备工作,为何要这样呢? 因为我们不是简单的说说directive怎么用,还要知道为什么这么用!(今天我们先磨磨刀!). 首先我们讲讲js 基础的知识--hasO ...

  5. ajax请求和aspx返回数据

    ajax请求: $(function () {            $.ajax({                url: "index.aspx?method=send",  ...

  6. Android中的沉浸式状态栏效果

    无意间了解到沉浸式状态栏,感觉贼拉的高大上,于是就是试着去了解一下,就有了这篇文章.下面就来了解一下啥叫沉浸式状态栏.传统的手机状态栏是呈现出黑色条状的,有的和手机主界面有很明显的区别.这一样就在一定 ...

  7. Dancing Links and Exact Cover

    1. Exact Cover Problem DLX是用来解决精确覆盖问题行之有效的算法. 在讲解DLX之前,我们先了解一下什么是精确覆盖问题(Exact Cover Problem)? 1.1 Po ...

  8. OpenWrt中开启usb存储和samba服务

    在从官网安装的WNDR3800 15.05.1版本OpenWrt中, 不带usb存储支持以及samba, 需要另外安装 1. 启用usb支持 USB Basic Support https://wik ...

  9. spring boot1

    spring boot 玩转spring boot--快速开始   开发环境: IED环境:Eclipse JDK版本:1.8 maven版本:3.3.9 一.创建一个spring boot的mcv ...

  10. 从零开始,DIY一个jQuery(3)

    在前两章,为了方便调试,我们写了一个非常简单的 jQuery.fn.init 方法: jQuery.fn.init = function (selector, context, root) { if ...