Android Window机制

Window层级结构

相关类简介

这里只介绍与UI层级相关的功能。

Window:抽象类,定义了window视图布局的一些规则,如是否显示ActionBar之类。

PhoneWindow:Window的唯一实现类,与Activity一一对应,持有了具体的View对象,对外提供操作这些View的具体实现。

DecorView:顶级View的具体实现,继承于FrameLayout,被PhoneWindow所持有。包含顶部状态栏与ContentView。

ContentView:这个不是一个具体存在的类,而是Framework中包含了很多已经实现的Content布局,根据用户的配置来选择加载合适的放置于DecorView中,例如有无ActionBar对应了两种不同的布局资源。(这里需要注意Activity.setContentView()设置的View是ContentView的子View而不是ContentView自己)

WindowUI层级

Window层级相关源码

Window:主要提供与DesorView相关的配置参数,如状态栏、ActionBar之类,具体源码不再赘述。

PhoneWindow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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.
// 对应上图的ContentView
ViewGroup mContentParent;

// 对应上图的TitleView
private TextView mTitleView;

// 这个方法实际上是Window中的,mContentParent、TitleView都是通过这个方法赋值的
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}

DecorView

1
2
3
4
5
6
7
// 对应上图的ContentView
ViewGroup mContentRoot;

// View added at runtime to draw under the status bar area
private View mStatusGuard;
// View added at runtime to draw under the navigation bar area
private View mNavigationGuard;

上述几个View的初始化流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 以Activity.setContentView()为起点
// Activity
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

// 以下代码为缩减并略作修改过的源码,便于阅读
// PhoneWindow
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// 根据设置的Window相关参数初始化DecorView
installDecor();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
}

// 根据设置的Window相关参数初始化DecorView
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 初始化并绑定mContentParent与DecorView中的容器View
mContentParent = generateLayout(mDecor);
}
}

protected ViewGroup generateLayout(DecorView decor) {
// 根据Window配置加载不同的系统内置ContentView布局
int layoutResource;
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if (condition2) {
// layoutResource = xxxx
}
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

// 取出ContentView中的content子View,也就是Activity.setContentView()设置的View的容器
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}

// 以下代码为缩减并略作修改过的源码,便于阅读
// DecorView
// 加载ContentView资源
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
// DecorCaptionView与多窗口功能有关,这里不做介绍
mDecorCaptionView = createDecorCaptionView(inflater);

// 加载ContentView资源
final View root = inflater.inflate(layoutResource, null);
mContentRoot = (ViewGroup) root;
}
setContentView流程图

上面的代码展示了setContentView()为起点的各个层级View的初始化过程,用图表示如下:

image-20180522213000456

一些总结

从源码分析可以得到以下结论:

  • View的根视图是DecorView
  • 一个Activity系统已有的视图层次是三层,DecorView->ContentView->content(开发者设置的View的容器)
  • 设置窗口属性需要在Activity.setContentView()之前
  • 最重要的是几个类在UI布局上的分工,Window提供DecorView配置的参数,PhoneWindow提供加载DecorView的具体实现,DecorView则控制状态栏等布局的具体实现。

WindowManager与Activity

这一节主要介绍WindowManager与Activity之间的关系,以及核心实例的创建过程。

相关类以及它们之间的联系

PhoneWindow:上面介绍过了,这里不再赘述。

WindowManager:The interface that apps use to talk to the window manager.

WindowManagerImpl:WindowManager和ViewManager的实现类,通过WindowManagerGlobal与WMS通信。

WindowManagerGlobal:单例,代理了WindowManagerImpl的功能,与WMS通信。全局管理所有的Window,相当于一个消息总线。

ViewRootImpl:与顶级View一一对应,绘制、触摸等事件由Window传递到View的中间节点。持有WindowSession通过Binder与WMS通信,同时持有IWindow作为WMS的回调接口,用于例如touch事件的回调。

用一张图表示他们之间的关系:

从源码分析相关实例创建流程

Activity与Window相关类是绑定在一起的,因此Window相关类的实例化都是融合在Activity的生命周期中的。

1、启动WMS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ActivityThread.java
// 前面的步骤不做叙述,直接从这个方法开始
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 这个方法启动了WMS
WindowManagerGlobal.initialize();

// 这里面处理的Activity resume之前的生命周期
Activity a = performLaunchActivity(r, customIntent);

// 启动Activity之后进入resume的流程,细节见后面的代码
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}

2、Activity的实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 通过反射实例化Activity
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
// ...
}

if (activity != null) {
// ....
// 进入Activity与Window的Attach流程
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);

// 进入Activity的onStart()生命周期
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
}

// 后面继续依次调用Activity的其他生命周期
}

3、Window相关类的创建以及它们与Activity的绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
// 实例化Window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
// 用于将Windw相关的事件,如触摸事件,传递给Activity
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);

// 实例化WindowManager并相互绑定
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
}

4、WindowManager的实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Manages all of the system services that can be returned by {@link Context#getSystemService}.
* Used by {@link ContextImpl}.
*/
final class SystemServiceRegistry {
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
new HashMap<Class<?>, String>();
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
static {
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
// 在这里被实例化,被所有Activity使用
return new WindowManagerImpl(ctx);
}});
}

private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
// 将token绑定到window上,标识当前window属于哪个Activity
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

从上面的代码可以看出,Activity与Window持有的是同一个WindowManager实例,但是这个实例不同于SystemServiceRegistry中注册的那个WindowManager实例,它们是父子关系

5、RootViewImpl的实例化

前面的代码实例化了Activity以及相关的Window类,接下来是处理View相关的逻辑。

先继续上面提到的ActivityThread.handleResumeActivity()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// ActivityThread.handleResumeActivity()
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward,
boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
// 这里会调用Activity.onResume()
r = performResumeActivity(token, clearHide, reason);
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为不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
// 将Window的DecorView绑定到Activity上,这时的DecorView已经添加过了我们在setContentView中添加的自定义布局
a.mDecor = decor;

// 是否设置了Window不可见
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// ViewRootImpl会在这个方法之后被实例化,详细过程见下一个代码块
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}

// 前面设置了DecorView不可见,这里重新设置为可见
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
}

// 最后一步
// Activity.setVisible()
void makeVisible() {
// mWindowAdded这个字段在前面被设置为true了,所以View不会被add两次
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
// 设置可见
mDecor.setVisibility(View.VISIBLE);
}
1
2
3
4
5
6
7
// WindowManagerImpl.addView()
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// ViewRootImpl最终在这个方法里被实例化
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

最后ViewRootImpl被实例化并和DecorView一起被全局保存在WindowManagerGlobal中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// WindowManagerGlobal.java
public final class WindowManagerGlobal {
// Window对应的根View
private final ArrayList<View> mViews = new ArrayList<View>();
// 与根View一一对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
// 与根View一一对应的Window参数
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();

public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

//将根View和ViewRootImpl绑定,调用的是view.assignParent(this),将
try {
// 在这个方法里调用的是view.assignParent(this),将ViewRootImpl设置为DecorView的parent
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
}
用一张图表示创建流程

补充:WindowManagerGlobal.addView()会调用到ViewRootImpl.setView(),并进一步调用到ViewRoot

Impl.requestLayout(),进入View的绘制流程,完成后设置DecorView可见。

image-20180528210349449

ViewRootImpl与View

上一节介绍了WindowManager与Activity的关系,并且提到了ViewRottImpl的实例化,这一节会更深入的了解ViewRootImpl,并以此介绍WindowManager与View绘制、以及事件分发处理的一些细节。

ViewRootImpl简介

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 WindowManagerGlobal.

作为View与绘制系统的桥梁,ViewRootImpl包含了View测量、布局、绘制的起点函数,同时,它也作为触摸事件传递的中介,将触摸信号由native传递到framework。

ViewRootImpl与View的绘制流程
  1. View的测量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // ViewRootImpl.requestLayout()
    public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
    // 在这里检查当前是否在主线程,防止在非主线程修改UI
    checkThread();
    mLayoutRequested = true;
    scheduleTraversals();
    }
    }

    以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performMeasure()调用到DecorView的measure()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
    return;
    }
    try {
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    }
  2. View的布局
    主要的流程与上述过程类似,以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performLayout()调用到DecorView的layout()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
    int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    mInLayout = true;

    final View host = mView;
    if (host == null) {
    return;
    }
    try {
    // 开始layout流程
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    mInLayout = false;
    // 这里的逻辑是处理在performLayout时,如果用户主动调用了requestLayout(),那么这个layout行为不会立即生效,而是记录在mLayoutRequesters中,在host.layout()结束后再逐一调用。关于requestLayout()的细节后文再作介绍。
    int numViewsRequestingLayout = mLayoutRequesters.size();
    if (numViewsRequestingLayout > 0) {
    for (int i = 0; i < numValidRequests; ++i) {
    final View view = validLayoutRequesters.get(i);
    view.requestLayout();
    }
    measureHierarchy(host, lp, mView.getContext().getResources(),
    desiredWindowWidth, desiredWindowHeight);
    mInLayout = true;
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
    }
  3. View的绘制
    主要的流程与上述过程类似,以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performDraw()调用到DecorView的draw()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private void performDraw() {
    // 标记是否需要重新绘制整个屏幕
    final boolean fullRedrawNeeded = mFullRedrawNeeded;
    mFullRedrawNeeded = false;
    mIsDrawing = true;
    try {
    // 这个方法会调用到DecorView
    draw(fullRedrawNeeded);
    } finally {
    mIsDrawing = false;
    }
    }
  4. View.requestLayout()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // View.requestLayout
    public void requestLayout() {
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
    ViewRootImpl viewRoot = getViewRootImpl();
    // 如上文所说的,如果正在layout步骤,则将该请求加入等待队列
    if (viewRoot != null && viewRoot.isInLayout()) {
    if (!viewRoot.requestLayoutDuringLayout(this)) {
    return;
    }
    }
    }
    // 一直往上调用mParent的requestLayout()
    if (mParent != null && !mParent.isLayoutRequested()) {
    mParent.requestLayout();
    }
    }

    由上面的代码可以知道,最终这个请求会调用到DecorView的requestLayout(),而DecorView的mParent就是RootViewImpl,因此layout的请求就会交由RootViewImpl来执行。

  5. View.invalidate()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // View.java
    public void invalidate() {
    invalidate(true);
    }
    void invalidate(boolean invalidateCache) {
    invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
    boolean fullInvalidate) {

    //这里判断该子View是否可见或者是否处于动画中
    if (skipInvalidate()) {
    return;
    }

    //根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
    if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
    || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
    || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
    || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
    // ...
    //把需要重绘的区域传递给父容器
    final AttachInfo ai = mAttachInfo;
    final ViewParent p = mParent;
    if (p != null && ai != null && l < r && t < b) {
    final Rect damage = ai.mTmpInvalRect;
    damage.set(l, t, r, b);
    //调用父容器的方法,向上传递事件
    p.invalidateChild(this, damage);
    }
    // ...
    }
    }

    与requestLayout的逻辑类似,invalidate过程会向上调用invalidateChild(),而invalidateChild()则会调用当前View的invalidateChildInParent(),这个方法的作用是计算需要绘制的子View在当前View中的位置。最后这两个方法都会调用到ViewRootImpl的同名方法中。

    在ViewRootImpl的invalidateChildInParent()中也会调用checkThread()进行线程判断,防止在非UI线程发起绘制请求。

    ViewRootImpl的invalidateChildInParent()最终依然会调用到scheduleTraversals方法,触发View的工作流程。不过由于没有添加measure和layout的标记位,因此measure、layout流程不会执行,而是直接从draw流程开始。

    总结一下就是每一层都需要做两件事,一个是计算需要绘制的View在当前层的坐标,一个是将绘制请求发往上一层View。

  6. ViewRootImpl.setView()
    上面的绘制流程是用户主动触发的一个流程,而在Activity的创建过程中,系统会触发一次整个流程,入口就是ViewRootImpl.setView()。这个方法会调用requestLayout()进入绘制流程。

ViewRootImpl与MotionEvent

image-20180604203347021

上述流程需要注意的几个点:

  • 在ViewRootImpl中,有一系列类似于InputStage(输入事件舞台)的概念,每种InputStage可以处理一定的事件类型,比如AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理。对于点击事件来说,ViewPostImeInputStage可以处理它。

  • 点击事件从DecorView传递到Activity的时候,注意到DecorView.dispatchTouchEvent()并没有调用super方法,因此避免了在此时将点击事件传入View树。这里的Callback就是Activity,绑定发生在attach()的时候。

    1
    2
    3
    4
    5
    6
    // DecorView.dispatchTouchEvent()
    public boolean dispatchTouchEvent(MotionEvent ev) {
    final Callback cb = getCallback();
    return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
    : super.dispatchTouchEvent(ev);
    }
  • 最后进入View树是在DecorView.superDispatchTouchEvent()中调用了父类,即View的dispatchTouchEvent方法。

一些总结
  • 布局绘制的请求发起必须要在主线程。
  • 不能在主线程中发起布局绘制请求的原因是requestLayout()和invalidateChildInParent()会checkThread。
  • 在onCreate()与onResume()过程中无法取得View的宽高值是因为绘制流程的开始是在onResume()之后。
  • 由于添加window的操作在requestlayout()之后,因此可以在onAttachedToWindow()时获取控件大小。

Window与WindowManagerService

上面介绍的是Window与Activity或者与View之间的关系,并没有介绍Window自己,下面的内容则是介绍Window自己的作用以及如何被WMS管理等。

Window

一个window恰如你在计算机中看到的一个window。它拥有唯一一个用以绘制自己的内容的surface。应用通过 Window Manager创建一个window,Window Manager 为每一个window创建一个surface,并把该surface传递给应用以便应用在上面绘制。应用可以在surface上任意进行绘制。对于Window Manager来说,surface就是一个不透明的矩形而已。

WindowManagerService作用
  • 为所有窗口分配Surface。客户端向WMS添加一个窗口的过程,其实就是WMS为其分配一块Suiface的过程,一块块Surface在WMS的管理下有序的排布在屏幕上。Window的本质就是Surface。
  • 管理Surface的显示顺序、尺寸、位置
  • 管理窗口动画
  • 输入系统相关:WMS是派发系统按键和触摸消息的最佳人选,当接收到一个触摸事件,它需要寻找一个最合适的窗口来处理消息,而WMS是窗口的管理者,系统中所有的窗口状态和信息都在其掌握之中,完成这一工作不在话下。
Window种类
  • 应用Window:与Activity一一对应,层级范围是1-99
  • 子Window:不能单独存在,需要依附在指定的父Window上,层级范围是1000-1999
  • 系统Window:需要声明权限才能创建,例如Toast就是系统Window,层级范围是2000-2999
Window与WMS的绑定

通过ViewRootImpl完成双向的通信

1
2
3
4
5
6
7
8
9
10
11
// ViewRootImpl.java
// mWindowSession的类型是IWindowSession它的实现类是Session,用来发送消息给WMS
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
}

// WMS回调的接口
final W mWindow;
static class W extends IWindow.Stub { }
Window的添加

image-20180607210338829

删除的过程比较类似,就不列出来了。

添加自己的Window
1
2
3
4
5
WindowManager wm = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
// 查文档并设置其他参数
params.type = WindowManager.LayoutParams.TYPE_PHONE;
wm.addView(view, params);

参考资料

Android开发艺术探索

Android窗口机制

android activity setContentView()方法执行流程分析

Android View 深度分析requestLayout、invalidate与postInvalidate

Android中MotionEvent的来源和ViewRootImpl

Android系统服务 —— WMS与AMS

Android易混概念辨析之Surface,Window,View,SurfaceView,Bitmap

Android 的窗口管理系统 (View, Canvas, WindowManager