1.系统窗口的添加过程
1.1例子
private void addStatusBarWindow() {
//获取状态栏的高度
final int height = getStatusBarHeight();
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,//宽度
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
//硬件加速
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
//gravity
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
//这个函数会通过inflate资源文件R.layout.super_status_bar来产生一个View并设置属性
makeStatusBarView();
//将StatusBarView加入到WMS中
mWindowManager.addView(mStatusBarWindow, lp);
}
1.2 WMS的获取
registerService(WINDOW_SERVICE, new ServiceFetcher() {
Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
Display display = ctx.mDisplay;
if (display == null) {
if (mDefaultDisplay == null) {
DisplayManager dm = (DisplayManager)ctx.getOuterContext().
getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
}
display = mDefaultDisplay;
}
return new WindowManagerImpl(display);
}});
1.3继承关系
WindowManagerImpl继承自WindowManager,WindowManager继承自ViewManager
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
1.4 流程
WindowManagerImpl的addView###
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerGlobal的addView###
WindowManagerGlobal是一个全局且单粒的对象
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;//WindowManagerImpl和ViewRoot在同一进程中
View panelParentView = null;
synchronized (mLock) {
//以前是否添加过这个View对象,findViewLocked从mViews查找是否有和view一样的即可
int index = findViewLocked(view, false);
if (index >= 0) {//已经添加过,禁止重复操作
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
//NOTE:根据这个view生成一个ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
//真正的跨进程操作开始
//这个场景中view对应PhoneStatusBar,params对应lp,panelParentView为空
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// 异常处理
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImpl的setView###
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;//一个ViewRoot对应一个ViewTree
requestLayout();//发起layout请求
try {
//调用Session接口
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
} catch (RemoteException e) {
}
}
}
}
ViewRootImpl作为ViewTree的管理者,同时担负着与WMS进行IPC通信的责任(通过IWindowSession来进行)
ViewRootImpl的构造函数中IWindowSession的调用流程###
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
//......
}
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
float animatorScale = windowManager.getAnimationScale(2);
ValueAnimator.setDurationScale(animatorScale);
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
}
return sWindowManagerService;
}
}
@Override
public IWindowSession openSession(IInputMethodClient client,
IInputContext inputContext) {
Session session = new Session(this, client, inputContext);
return session;
}
Session的addToDisplay方法###
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel);
}
//Session的继承关系
final class Session extends IWindowSession.Stub
addWindow方法###
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
int[] appOp = new int[1];
//权限检查
int res = mPolicy.checkAddPermission(attrs, appOp);
WindowState attachedWindow = null;
WindowState win = null;//WindowState用于记录一个Window
long origId;
final int type = attrs.type;//窗口类型
synchronized(mWindowMap) {
//避免重复添加:根据IBinder查询WindowState是否已经存在
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//类型的值在子窗口的开始值到子窗口的结束值之间,也就是属于子窗口的话
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//寻找父窗口
attachedWindow = windowForClientLocked(null, attrs.token, false);
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
//根据不同窗口类型检查有效性
if (token == null) {
} else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
} else if (type == TYPE_INPUT_METHOD) {
} else if (type == TYPE_WALLPAPER) {
} else if (type == TYPE_DREAM) {
}
//WindowState
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (win.mDeathRecipient == null) {
//客户端已经死亡,不需要往下执行
return WindowManagerGlobal.ADD_APP_EXITING;
}
//调整Window属性
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);
res = WindowManagerGlobal.ADD_OKAY;
origId = Binder.clearCallingIdentity();
if (addToken) {//前面的步骤新增了token
mTokenMap.put(attrs.token, token);//把token添加到mTokenMap中
}
win.attach();
//<IBinder,WindowState>
mWindowMap.put(client.asBinder(), win);
boolean imMayMove = true;
//把所有window按顺序排列
if (type == TYPE_INPUT_METHOD) {
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
} else {
//新窗口按顺序添加到WindowList中
addWindowToListInOrderLocked(win, true);
}
win.mWinAnimator.mEnterAnimationPending = true;
if (displayContent.isDefaultDisplay) {
//ContentInset计算
mPolicy.getContentInsetHintLw(attrs, outContentInsets);
} else {
outContentInsets.setEmpty();
}
//分配层值
assignLayersLocked(displayContent.getWindowList());
}
return res;
}
1.首先进行权限检查,如果是非系统窗口直接返回ADD_OKAY;如果添加的是系统窗口需要进一步细化,比如TYPE_TOAST虽然是系统窗口,但应用程序有权限创建;而TYPE_PHONE,TYPE_SYSTEM_ERROR等窗口类型就需要相应的权限许可才能使用(permission.SYSTEM_ALERT_WINDOW)
2.mWindowMap的类型是HashMap<IBinder,WindowState>,前面的IBinder代表IWindow,所有mWindowMap是WMS中与此IWindow相对应的WindowState的映射。一个IWindow只允许添加唯一的窗口,否则函数直接报错返回,当然一个应用进程中可以持有多个ViewRoot对象,也就意味着它所管理的ViewTree理论上并没有数量上的限制。
3.如果要添加的窗口类型是子窗口,还要先找它的父窗口,而且需要注意父窗口本身不能是其他窗口的子窗口,否则添加失败。具体的实现是windowForClientLocked
-
token类型是WindowToken
-
attrs.token类型是IBinder,它代表了这个窗口的主人,AMS为每个Activity都分别创建了一个ActivityRecord对象,其本质是一个IBinder,在启动Actiity的时候,这个token会被自动赋值
-
addToken用于指示后续操作中有没有新的Token被添加
TU 10-8
如果attrs.token在mTokenMap中找不到对应的WindowToken,说明它在AMS中没有记录此时分为两种情况
情况一:如果是以下几种窗口类型,则必须在AMS中有案可查,也就是说这些类型的窗口必须实现已经在mTokenMap中添加了对应关系,这是由AMS来完成的,如果此时找不到,说明某些步骤出现了问题,所以程序报错返回
[FIRST_APPLICATION_WINDOW,LAST_APPLICATION_WINDOW]
TYPE_INPUT_METHOD
TYPE_WALLPAPER
TYPE_DREAM
情况二:除上述的窗口类型外,允许在AMS中没有备案,接着程序会主动为整个窗口生成一个WindowToken,并且置addToken为true
-
如果在mTokenMap可以找到对应的WindowToken(只有情况一的那几种窗口类型才有可能在mTokenMap中事先添加了对应关系),需要做进一步检查,比如在ApplicationWindow的情况下token.appWindowToken不能为空。除此之外,ViewRoot所指示的windowType必须和AMS中当时登记的类型token.windowType完全一致,否则会出现ADD_BAD_APP_TOKEN错误
-
如果一切顺利,WMS会为这个窗口添加一个WindowState;this表示WMS,sessino是WMS提供给窗口使用者的IWindowSession,client则是IWindow,即窗口使用者提供给WMS的访问通道,token是WindowToken,attachedWindow代表父窗口(如果存在的话)
-
如果客户端已经死亡,就不需要往下执行
-
调用Policy进行窗口参数调整,此时Policy会对某些情况进行约束
-
如果前面几步中新增了Token对象,那么此时需要将它加入到全局HashMap中,即mTokenMap
-
因为我们新增一个Window,很有可能影响之前已经排列好的WindowList,所以需要将此新窗口按顺序添加到WindowList中,另外如果用户指定了FLAG_SHOW_WALLPAPER或者本身就是壁纸类型的窗口,还要调用ajustWallpaerWindowsLocked进行壁纸调整
-
getContentInsetHintLw用于计算窗口的ContentInset区域
-
assignLayersLocked负责给上一步中已经排好序的WindowList中的窗口一一分配最终的层级值
addWindowToListInOrderLocked方法###
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
if (win.mAttachedWindow == null) {//没有父窗口的话
final WindowToken token = win.mToken;
int tokenWindowsPos = 0;
if (token.appWindowToken != null) {
tokenWindowsPos = addAppWindowToListLocked(win);
} else {//appWindowToken为空的情况,
addFreeWindowToListLocked(win);
}
if (addToToken) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(tokenWindowsPos, win);
}
} else {
addAttachedWindowToListLocked(win, addToToken);
}
if (win.mAppToken != null && addToToken) {
win.mAppToken.allAppWindows.add(win);
}
}
addFreeWindowToListLocked方法###
private void addFreeWindowToListLocked(final WindowState win) {
final WindowList windows = win.getWindowList();
final int myLayer = win.mBaseLayer;
int i;
//找到合适的位置
for (i = windows.size() - 1; i >= 0; i--) {
if (windows.get(i).mBaseLayer <= myLayer) {
break;
}
}
i++;
//添加到WindowList中
windows.add(i, win);
//mWindowsChanged置为true表示windowlist发生改变
mWindowsChanged = true;
}
总体流程就是为整个窗口创建一个与之对应的WindowState,然后加入到WMS的WindowList中
2.Activity窗口的添加过程
WMS并不会区分窗口的使用者是谁,只是在层级和权限的处理上会有所不同;当用户启动一个Activity的时候,AMS首先判断该Activity所属的进程是否已经在运行,如果是就向这个进程发送启动指定Activity的命令,否则就先创建该应用进程,运行ActivityThread主线程,然后处理启动Activity的操作。当AMS发现一个Activity即将启动时,会把相关信息告知WMS。
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
TaskRecord task = null;
if (!newTask) {
boolean startIt = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
task = mTaskHistory.get(taskNdx);
if (task == r.task) {
if (!startIt) {
//addAppToken 把ActivityRecord的相关信息传递过去
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
r.userId);
return;
}
break;
} else if (task.numFullscreen > 0) {
startIt = false;
}
}
}
//....
}
@Override
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId) {
synchronized(mWindowMap) {
AppWindowToken atoken = findAppWindowToken(token.asBinder());
if (atoken != null) {
return;
}
//这里记录的都是ActivityRecord的信息
atoken = new AppWindowToken(this, token);
atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
atoken.groupId = taskId;
atoken.appFullscreen = fullscreen;
atoken.showWhenLocked = showWhenLocked;
atoken.requestedOrientation = requestedOrientation;
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
task = createTask(taskId, stackId, userId, atoken);
} else {
task.addAppToken(addPos, atoken);
}
//加入<IBinder,WindowToken>
mTokenMap.put(token.asBinder(), atoken);
atoken.hidden = true;
atoken.hiddenRequested = true;
}
}
把ActivityRecord记录到<IBinder,WindowToken>,如果没有这一步,activity调用addWindow会失败###
handleResumeActivity
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
//这句话导致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();
//DecorView是Activity的整棵ViewTree的最外围
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);//可见性
//获取WindowManger
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();//获取属性
a.mDecor = decor;
//窗口类型
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);//将窗口添加到WMS中
}
//......
}
也是调用addWindow来处理,WMS会为他们生成WindowState,区别在于添加到WindowList###
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
if (DEBUG_FOCUS_LIGHT) Slog.d(TAG, "addWindowToListInOrderLocked: win=" + win +
" Callers=" + Debug.getCallers(4));
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
int tokenWindowsPos = 0;
if (token.appWindowToken != null) {//appWindowToken的情况
tokenWindowsPos = addAppWindowToListLocked(win);
} else {
addFreeWindowToListLocked(win);
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
}
} else {
addAttachedWindowToListLocked(win, addToToken);
}
if (win.mAppToken != null && addToToken) {
win.mAppToken.allAppWindows.add(win);
}
}
addAppWindowToListLocked方法###
private int addAppWindowToListLocked(final WindowState win) {
//WindowState包含了IWindow信息和WindowToken信息
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
//系统当前所有窗口
final WindowList windows = win.getWindowList();
final int N = windows.size();//当前所有窗口数量
WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
int tokenWindowsPos = 0;
int windowListPos = tokenWindowList.size();
if (!tokenWindowList.isEmpty()) { //该应用程序已经包含有一个窗口
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// Base windows go behind everything else.
WindowState lowestWindow = tokenWindowList.get(0);
placeWindowBefore(lowestWindow, win);
tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
} else {
//...............
}
return tokenWindowsPos;//位置
}
WindowState pos = null;
//该应用程序没有窗口的时候找到合适的位置插入
windows.add(i, win);
mWindowsChanged = true;
return tokenWindowsPos;
}
网友评论