Android4.4 窗口添加过程
注:本文参考《深入理解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 SYSTEM_SERVICE_MAP = new HashMap();
以服务名为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进程和系统进程的纽带。
二:activity添加窗口
更多相关文章
- Android可定制 圆形/圆角 Imageview(RoundedImageView)
- android 常见的一些异常和错误
- Android(安卓)ImageButton Example 图片按钮
- Android(安卓)之窗口小部件详解--App Widget
- Activity(启动模式) Activity(退出)
- android 数据持久化——I/O操作
- Android(安卓)添加图标
- Android轻松实现多语言的方法示例
- Android中传递对象的三种方法的实现