注:本文参考《深入理解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添加窗口








































更多相关文章

  1. Android可定制 圆形/圆角 Imageview(RoundedImageView)
  2. android 常见的一些异常和错误
  3. Android(安卓)ImageButton Example 图片按钮
  4. Android(安卓)之窗口小部件详解--App Widget
  5. Activity(启动模式) Activity(退出)
  6. android 数据持久化——I/O操作
  7. Android(安卓)添加图标
  8. Android轻松实现多语言的方法示例
  9. Android中传递对象的三种方法的实现

随机推荐

  1. Android(安卓)Web App官方文档翻译第二章
  2. Android(安卓)View 如何去自定义View
  3. [置顶] Android短信息验证码自动填写详细
  4. Android(安卓)开发 —— Handler的使用
  5. Android的界面设计规范-5
  6. Android(安卓)APK安装后点击[打开]与[完成]的
  7. Android中asset文件夹和raw文件夹区别
  8. Android学习之Activity伪装成对话框形式)
  9. Flutter 插件开发:以微信SDK为例
  10. Delphi XE7下如何创建一个Android模拟器