WindowManager添加Window的实现
Button mFloatButton = new Button(this);
mFloatButton.setText("button");
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
//mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mWindowManager.addView(mFloatButton, mLayoutParams);
1、Flags参数,表示Window的属性:
-
FLAG_NOT_FOCUSABLE:
表示 Window不需要获取焦点,不需接收各种输入事件,此标记会同时启用 FLAG_NOT_TOUCH_MODAL
事件会直接传递给下层的具有焦点的 Window -
FLAG_NOT_TOUCH_MODAL:
当前 Window 区域外的单击事件传递给底层的Window,
当前Window区域内的单击事件则自己处理。
一般都需要开启此标记,否则其他Window将无法收到单击事件。 -
FLAG_SHOW_WHEN_LOCKED:
让 Window 显示在锁屏的界面上
2、Type参数,表示Window的类型
Window有三种类型,分别对应【应用Window、子Window、系统Window】
- 应用类Window,对应一个Activity
- 子Window,不能单独存在,需依附特定的父Window中
比如,Dialog就是一个子Window。 - 系统Window,需要声明权限才能创建,
比如,Toast、系统状态栏 都是系统Window。
3、Window 的分层
每个Window 都有对应的 z-ordered,层级大的,覆盖在层级小的 Window 上面
- 应用Window 层级范围 1-99
WindowManager.FIRST_APPLICATION_WINDOW - WindowManager.LAST_APPLICATION_WINDOW - 子Window 层级范围 1000-1999
WindowManager.FIRST_SUB_WINDOW - WindowManager.LAST_SUB_WINDOW - 系统Window 层级范围 2000-2999
WindowManager.FIRST_SYSTEM_WINDOW - WindowManager.LAST_SYSTEM_WINDOW
想要 Window 位于所有Window的最顶层,采用最大的层级即可
- 设置type为系统Window
需要权限
<uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”/>
4、WindowManager 功能 添加View、更新View、删除View
public interface ViewManager {
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
public interface WindowManager extends ViewManager {
}
可拖动的Window,实现:
根据手指的位置来设定 LayoutParams 中的 x、y的值。
首先给View 设置 OnTouchListener ,然后在 onTouch方法中更新View的位置
mWindowManager.updateViewLayout()
public boolean onTouch(View view, MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch(event.getAction() {
case MotionEvent.ACTION_MOVE:
mLayoutParams.x = rawX;
mLayoutParams.y = rawY;
mWindowManager.updateViewLayout(mFloatButton, mLayoutParams);
break;
}
return false;
}
Window内部机制
Window是一个抽象的概念,每个Window,都对应着一个 View 和一个 ViewRootImpl
Window 和 View 通过 ViewRootImpl 来建立联系
Window并不实际存在,它是以View的形式存在的,View在是Window存在的实体
1、Window 的添加过程
WindowManagerImpl 中,把操作委托给了 WindowManagerGlobal
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
public final class WindowManagerGlobal {
// 存储所有Window 对应的 View
private final ArrayList<View> mViews = new ArrayList<View>();
// 存储所有Window 对应的 ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
// 存储所有Window 对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
// 存储正在被删除的 View 对象,那些调用了 removeView 方法,但是删除操作还未完成的Window对象
private final ArraySet<View> mDyingViews = new ArraySet<View>();
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
//1、检查参数是否合法
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// 如果是 子Window 需要调整布局参数
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
...
//2、创建 ViewRootImpl 并将 View 添加到列表中
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//3、通过 ViewRootImpl 更新界面并完成Window添加过程
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
ViewRootImpl.setView()中,通过 WindowSession 完成 Window 的添加
IWindowSession 【是一个Binder 对象,真正实现类是 com.android.server.wm.Session】
Window 的添加过程是一次 IPC 调用
public final class ViewRootImpl implements ViewParent, ... {
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
//完成View 的绘制
requestLayout();
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
/* 通过 WindowSession 完成 Window 的添加
* IWindowSession 【是一个Binder 对象,真正实现类是 Session】
* com.android.server.wm.Session
* Window 的添加过程是一次 IPC 调用
*/
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
}
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//开始绘制 View
scheduleTraversals();
}
}
}
IWindowSession 【是一个Binder 对象,真正实现类是 com.android.server.wm.Session】
package com.android.server.wm.Session;
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
// 调用 WindowManagerService.addWindow()实现添加
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
}
最终 Window 的添加交给了WindowManagerService.addWindow();处理。
WindowManagerService中,会为每个应用保留一个单独的 Session。
Window添加过程
2、Window 的删除过程
public final class WindowManagerGlobal {
// 存储所有Window 对应的 View
private final ArrayList<View> mViews = new ArrayList<View>();
// 存储所有Window 对应的 ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
// 存储所有Window 对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
// 存储正在被删除的 View 对象,那些调用了 removeView 方法,但是删除操作还未完成的Window对象
private final ArraySet<View> mDyingViews = new ArraySet<View>();
// 删除
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// 从 mViews 中找到对应的 View 索引
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
// 根据索引删除View
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
// 根据View 的索引,找到 对应的 ViewRootImpl
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
// ViewRootImpl.die() 删除View
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {//如果是异步删除,
//将View,添加到 mDyingViews 待删除列表
mDyingViews.add(view);
}
}
}
}
public final class ViewRootImpl implements ViewParent, ... {
/*
* immediate 表示是否立即删除;
* true 立即删除,表示表示同步删除
* false 异步删除
*/
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
// 立即删除,直接调用 doDie()方法执行删除
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
/* 如果是异步删除,发送一个 MSG_DIE 给 ViewRootImpl 的 ViewRootHandler
* handler 收到 消息后,执行 doDie() 方法
*/
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
}
doDie()内会调用 dispatchDetachedFromWindow() 方法,真正删除View的逻辑在 dispatchDetachedFromWindow 中。
dispatchDetachedFromWindow:
i. 垃圾回收相关工作,清除数据和消息、移除回调
ii. 通过Sess