注:本文参考《深入理解Android内核设计思想》10.3节窗口的添加过程
窗口添加分两类:service和activity窗口添加,先以systemUI中的statusbar作为例子说明大致过程,然后再分析activity的不同。
一:状态栏的添加
在statusbarview.java中
private void addStatusBarWindow() {
// Put up the view
final int height = getStatusBarHeight();
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
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.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
makeStatusBarView();//创建一个view
mWindowManager.addView(mStatusBarWindow, lp);
}
接下来看看这个mWindowManager是什么,addView是什么
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
在Contextimpl.java中
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
SYSTEM_SERVIEC_MAP是一个Map
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
new HashMap<String, ServiceFetcher>();
以服务名为key,servicefetcher为元素。猜测是放置了WINDOW_SERVICE。
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);
}});
通过registerService将WINDOW_SERVICE放入了SYSTEM_SERVICE_MAP中,其实可以看出,最后获得的mWindowManager其实是一个WindowManagerImpl,并且这个WindowManagerImpl是在systemUI进程中的,进入其addView方法:
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
调用mGlobal的addView方法,继续跟
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
可知,其为单例模式,进程唯一的。
private WindowManagerGlobal() {
}
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
在其构造方法中什么也没做,直接进入addView:
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//对添加的view做检查,如果包含该view则报异常
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.
}
//注意这是在单例模式中的方法,所以应用可以多次addView,但是所有的view root等都是在一个类中管理的。
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//在应用进程中保存创建的view、root和wparams等对象
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
//跨进程之旅从此开始
root.setView(view, wparams, panelParentView);
}
进入viewrootimpl的setView方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;//这个view其实是个viewroot,即根view,需要保存。
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//重新layout,
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();//跨进程调用开始了
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
}
}
首先看看mWindowSession对象,然后进入addToDisplay方法:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
....
}
在ViewRootImpl的构造方法中初始化了该对象,调用了WindowManagerGlobal的方法。原来以为这个mWindowSession也是保存在WindowManagerGlobal中的,进程唯一的,后来发现是错的。
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) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
调用WMS的openSession方法,获得这个WindowSession对象,代码如下:
@Override
public IWindowSession openSession(IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, client, inputContext);
return session;
}
new了一个Session对象,将client和WMS传入,这样可以通过Session调用WMS,可以知道Session是ViewRootImpl在WMS的代表,他们是一一对应的,即ViewRootImpl通过IWindowSession接口和WMS通信,进入其addToDisplay方法:
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);
}
不出所料,调用了WMS的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);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null;
long origId;
final int type = attrs.type;
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent == null) {//显示屏幕不存在
Slog.w(TAG, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (!displayContent.hasAccess(session.mUid)) {
Slog.w(TAG, "Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {//该窗口已经存在,不能重复添加
Slog.w(TAG, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口,需要先找出父窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow == null) {
Slog.w(TAG, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (win.mDeathRecipient == null) {//如果客户端死亡,不用添加了
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
origId = Binder.clearCallingIdentity();
if (addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();//将win添加到wms,其实就是将这个Session放入WMS的mSession中
mWindowMap.put(client.asBinder(), win);//放入mWindowMap中
if (win.mAppOp != AppOpsManager.OP_NONE) {
if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())
!= AppOpsManager.MODE_ALLOWED) {
win.setAppOpVisibilityLw(false);
}
}
boolean imMayMove = true;
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);//排序
if (type == TYPE_WALLPAPER) {
mLastWallpaperTimeoutTime = 0;
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (mWallpaperTarget != null
&& mWallpaperTarget.mLayer >= win.mBaseLayer) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
assignLayersLocked(displayContent.getWindowList());//分配最终层级
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
Binder.restoreCallingIdentity(origId);
return res;
}
来分析下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) {//a如果是activity添加的窗口
tokenWindowsPos = addAppWindowToListLocked(win);
} else {
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);
}
}
private void addFreeWindowToListLocked(final WindowState win) {
final WindowList windows = win.getWindowList();//获取该屏幕上的window列表
// Figure out where window should go, based on layer.
final int myLayer = win.mBaseLayer;
int i;
for (i = windows.size() - 1; i >= 0; i--) {
if (windows.get(i).mBaseLayer <= myLayer) {//找到刚好小于或者等于的位置
break;
}
}
i++;//向上一位肯定是小于了
windows.add(i, win);//插入
mWindowsChanged = true;
}
至此,窗口添加成功。
小结:
1.WindowManager其实就是一个工具,在进程中调用其addView方法将view添加到WMS中,这个接口的实现是WindowManagerImpl类中封装了WindowManagerGlobal(mGlobal)来实现的。
2.WindowManagerGlobal:从名字就可以看出来这是个单例模式的类,在进程中都可以调用。这个类通过三个数组保存进程中所添加的窗口:mRoots数组放置了进程中new的所有ViewRootImpl对象;mViews数组放置了进程中添加的view对象;mParams数组放置了进程中窗口参数,并且这三个数组的index是表示同一个对象。
3.ViewRootImpl:这个类是核心,其中的mWindowSession和WMS交互;mSurface负责内存相关;mView负责显示内容;mWindow传入WMS,可以接收WMS的回调。从上可知该类是联系app进程和系统进程的纽带。