Android Window

Posted by Jfson on 2018-01-10
  • 1.什么是Window

    • 1.1、代码层面来看,Window是一个abstract类。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /**
      * 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.
      *
      * <p>The only existing implementation of this abstract class is
      * android.view.PhoneWindow, which you should instantiate when needing a
      * Window.
      */
      public abstract class Window {
      ...
      }
    • 1.2、解释

      • Window是为顶级View提供的一个抽象类,该实现类应该作为顶级的view被添加到WindowManager中
      • WindowManager提供了标准的ui规则,比如:背景,标题等
      • 仅仅有一个存在的实例PhoneWindow,当需要Window时,实例PhoneWindow即可。

    2.什么是WindowManager

    • 2.1、直接看WindowManager interface的实现类
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    /**
    * 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.
    */
    public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;
    private IBinder mDefaultToken;
    public WindowManagerImpl(Display display) {
    this(display, null);
    }
    private WindowManagerImpl(Display display, Window parentWindow) {
    mDisplay = display;
    mParentWindow = parentWindow;
    }
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mDisplay, parentWindow);
    }
    public WindowManagerImpl createPresentationWindowManager(Display display) {
    return new WindowManagerImpl(display, mParentWindow);
    }
    /**
    * Sets the window token to assign when none is specified by the client or
    * available from the parent window.
    *
    * @param token The default token to assign.
    */
    public void setDefaultToken(IBinder token) {
    mDefaultToken = token;
    }
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
    }
    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
    // Only use the default token if we don't have a parent window.
    if (mDefaultToken != null && mParentWindow == null) {
    if (!(params instanceof WindowManager.LayoutParams)) {
    throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    // Only use the default token if we don't already have a token.
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (wparams.token == null) {
    wparams.token = mDefaultToken;
    }
    }
    }
    @Override
    public void removeView(View view) {
    mGlobal.removeView(view, false);
    }
    @Override
    public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
    }
    @Override
    public Display getDefaultDisplay() {
    return mDisplay;
    }
    }
    • 2.2、解释

      • WindowManager 是用来提供低等级的通讯跟system window manager 。例如:绑定context/显示/父Window的操作
      • 允许添加任何的View作为高等级的Window。
      • 一般我们不会直接用WindowManager,通常在Activity、Dialog上使用
    • 2.3、功能

      • addView() 添加View
      • updateViewLayout() 刷新布局
      • applyDefaultToken()
      • removeView()
      • 以上所有操作均是通过单例类 WindowManagerGlobal 完成

    3.什么是WindowManagerGlobal

    • 3.1、WindowManagerGlobal是App中全局的窗口管理模块
    • 3.2、WindowManagerGlobal 主要用来与WMS进行交互。
    • 3.3、那么问题来了怎么跟WMS交互?
      • 通过Binder进程间的通信获取WMS的session
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
    if (sWindowSession == null) {
    try {
    InputMethodManager imm = InputMethodManager.getInstance();
    // a. 获取WMS
    IWindowManager windowManager = getWindowManagerService();
    // b.拉取session
    sWindowSession = windowManager.openSession(
    new IWindowSessionCallback.Stub() {
    @Override
    public void onAnimatorScaleChanged(float scale) {
    ValueAnimator.setDurationScale(scale);
    }
    },
    imm.getClient(), imm.getInputContext());
    } catch (RemoteException e) {
    Log.e(TAG, "Failed to open window session", e);
    }
    }
    // c.返回WMS中的session
    return sWindowSession;
    }
    }
    public static IWindowManager getWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
    if (sWindowManagerService == null) {
    // 进程间通信 binder使用
    sWindowManagerService = IWindowManager.Stub.asInterface(
    ServiceManager.getService("window"));
    try {
    sWindowManagerService = getWindowManagerService();
    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
    } catch (RemoteException e) {
    Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
    }
    }
    return sWindowManagerService;
    }
    }
    public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow) {
    ...
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
    // Start watching for system
    // The previous removeView() had not completed executing. Now it has.
    // ...
    // If this is a panel window, then
    // a.实例该View的ViewRootImpl
    root = new ViewRootImpl(view.getContext(), display);
    // b.设置布局位置参数
    view.setLayoutParams(wparams);
    // c.存储view,ViewRootImpl,params
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    }
    // do this last because it fires off messages to start doing things
    // d.将view添加到(显示View)
    root.setView(view, wparams, panelParentView);
    }
    • 3.4、WMS 中的session 存储在了WindowManagerGlobal单例类的实例中,那么如何用到的呢。在addView()中,最后看到 root.setView(view, wparams, panelParentView)了,那么ViewRootImpl到底是什么?

    4.什么是ViewRootImpl

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 构造中WindowManagerGlobal.getWindowSession() 获取session
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
...
//a.请求layout
requestLayout();
//b.通过session去通知WMS显示mWindow
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 检测线程
checkThread();
mLayoutRequested = true;
// 各种通知
scheduleTraversals();
}
}
void checkThread() {
// 只能是当前ViewRootImpl
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// mTraversalRunnable 调用doTraversal()到 performTraversals()中进行View的绘制
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
private void performTraversals() {
// 各种view change状态检查
...
// measure
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)
// 该dispatchOnGlobalLayout通常用来接收View measure完毕,获取view宽高
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
...
// draw
performDraw();
}
1
2
3
4
5
6
7
// dispatchOnGlobalLayout的使用
view.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//measure完毕
}
});
  • 4.1、ViewRootImpl 中获取WindowManagerGlobal中的session,改session 是通过Binder获取WMS中的,在ViewRootImpl进行线程检测、layout,measure,draw等一系列操作。最终ViewRootImpl通过session去通知WMS显示mWindow。

    总结

    image


pv UV: