本来是要讲 activity 的window创建过程,但首先得先了解一些 window 的知识,然后才来介绍会比较好点。

一 . Window 介绍

在我们日常开发中,Window 看似接触的不多,实际上,Android 中所有的视图都是通过 Window 来呈现的,不管是 Activity,Dialog、Taost 还是 PupopWindow ,它们的视图其实都是附加在 Window 上的。

Window 是一个抽象类,它的创建类是 PhoneWindow,它的具体实现类位于 WindowManagerService中;
每个 window 都对应一个 View 和一个 ViewRootImpl, window 和 view 是通过 ViewRootImpl来 来建立联系的,因此 window 并不是实际存在,它是以 view 的形式存在。

这点从 windowmanager 的定义也能看出来,它的三个接口方法,addView,updateView,removeView 都是针对 view 的,这说明 view 才是 window 的实体。

1.1. Window 和 WindowManager

上面说到,创建一个 window 可以通过 WindowManager 来实现,比如简单添加一个 TextView:

        TextView textView = new TextView(this);        textView.setText("测试");        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);        WindowManager.LayoutParams params = new WindowManager.LayoutParams();        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;        params.format = PixelFormat.TRANSLUCENT;        params.width = 200;        params.height = 200;        params.gravity = Gravity.START|Gravity.TOP;        wm.addView(textView,params);

上面将一个 textview 放到左上角的位置,并规定大小为 200x200;
其中,params 中,flags 和 type 比较重要。

1.1.1 flags

其中,flags 参数表示 window 的属性,它有很多属性,这里只简单介绍几个,其他请自行查看官方文档:

  • FLAG_NOT_TOUCH_MODAL: 触摸事件在 window 的区域内,区域外的传递给底层的 window。
  • FLAG_NOT_FOCUSABLE: 表示 window 不需要获取焦点,也不需要接受各种输入事件,次标记会同时启用 FLAG_NOT_TOUCH_MODAL ,最终事件会传递给下层的具有焦点的 window;这个要特别注意的,如果你需要点击事件,不要使用该标志。
  • FLAG_SHOW_WHEN_LOCKED:开启次模式可以让 Window 显示在锁屏的界面上

1.1.1 type

Type 表示 Window 的类型,window 分为3中类型,分别是

  • 应用 window : 比如 activity
  • 子 window:子 window 不能单独存在,比如 dialog
  • 系统 window :需要申明权限才能创建的window,比如在service 中,弹出一个dialog,就需要配置 WindowManager.LayoutParams 的 type 为系统级别的;Toast 也是个系统级别的 window。

window 也是分层的,每个window都有一个 z-ordered,层级大的覆盖在层级小的window上面。其中:

  • 应用 window:层级范围 1-99
  • 子 window :层级范围为 1000~1999
  • 系统 window:基层范围是 2000~2999

如果想要 Window 位于所有 Window 的最顶层,那么采用较大的层级即可。很显然,系统的 window 的层级是最大的,一般我们可以使用 TYPE_SYSTEM_OVERLAY 或者 其他的,但是要记得加权限:

        

不然系统 window 会报错的。

二、Window 的内部实现

Window 是一个抽象的概念,每一个 Window 都对应一个 View 和 一个 ViewRootImpl,Window 和 View 是通过 ViewRootImpl 来建立联系的,因此 Window 并不存在,而是以 View 的形式存在的。

2.1 Window 的添加过程

在实际使用中,我们无法访问 window,只能通过 WindowManager,它是window 访问的入口。WindowManager 是一个接口, 它的具体实现类是 WindwoManagerImpl 类中,其中添加View addView()、更新View updateViewLayout()、删除View removeView() 的方法如下:

    @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);    }

可以看到,windowmanagerlmpl 并没有直接实现 window 的三大操作,而是交给了 WindowManagerGlobal :

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

进入mGlobal 的 addview() 查看:

    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);        }....            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);            // do this last because it fires off messages to start doing things            try {                root.setView(view, wparams, panelParentView);            } catch (RuntimeException e) {                // BadTokenException or InvalidDisplayException, clean up.                if (index >= 0) {                    removeViewLocked(index, true);                }                throw e;            }        }    }

可以看到,如果是 子window ,还需要调整一些参数布局:

     final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        }

接着最重要的一点是,创建 ViewRootImpl,并将 view 添加到列表中。

那么 mViews 、mRoots 是什么意思呢?在 WindowManagerGlobal 中,有几个比较重要的列表,如下:

    private final ArrayList mViews = new ArrayList();    private final ArrayList mRoots = new ArrayList();    private final ArrayList mParams =            new ArrayList();    private final ArraySet mDyingViews = new ArraySet();

其中

  • mViews 存储所有 window 所对应的 view
  • mRoots 存储的是所有 window 所对应的 ViewRootImpl
  • mParams 存储所有 window 对应的布局参数
  • mDyingViews 则存储了哪些正在啊被删除的 View 对象。

我们都知道,View 的绘制过程是由 ViewRootImpl 来完成的,这里也不例外,在 setView() 中,会完成异步绘制的流程,setView 的代码比较多,它里面会对参数进行判断,并可以通过 requestLayout() 方法完成异步刷新请求:

    @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }

scheduleTraversals 才是 View 的绘制入口,会执行 mChoreographer.postCallback 绘制;
下一次的垂直信号过来的时候,执行 mTraversalRunnable ,它的实现类为 TraversalRunnable ,在 run 方法中执行 doTraversal()

#ViewRootImpl#pokeDrawLockIfNeeded    void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);         ...     }#ViewRootImpl#TraversalRunnable     final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }#ViewRootImpl#doTraversal    void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);            if (mProfile) {                Debug.startMethodTracing("ViewAncestor");            }            performTraversals();....    }

其中 performTraversals 函数比较多,这里列出它的主要工作好了,具体细节看源码:

  1. 当第一次调用时,执行 dispatchAttachedToWindow -> view.onAttachedToWindow
  2. 拿到 window 的参数,通过调用 relayoutWindow -> mWindowSession.relayout(),请求 WindowManaagerService 来计算窗口大小,内容区域大小等。
  3. 调用 performMessure -> mView.measure() -> onMeasuse() 递归测量 View 的大小
  4. performLayout -> mView.layout -> onLayout , 递归执行布局
  5. performDraw -> mView.draw -> onDraw , 递归执行绘制

在 setView 执行完 requestLayout()之后,会调用以下代码:

try {   mOrigWindowType = mWindowAttributes.type;    mAttachInfo.mRecomputeGlobalAttributes = true;    collectViewAttributes();    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,            mTempInsets);    setFrame(mTmpFrame);} 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);} finally {    if (restore) {        attrs.restore();    }}

可以可以看到,window 的最后显示,是通过 mWindowSession.addToDisplay() 去实现的,而 mWindowSession 的实现类为 Session ,是典型的Binder机制:

    @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,            InsetsState outInsetsState) {        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,                outInsetsState);    }

mService 为 WindowmanagerService,它会调用 addWindow 方法实现 window 的添加。

所以,window 的最终实现,还是在 WindwoManagerService;通过上面的源码观察,我们也可以发现,Window 和 View 的关联是通过 ViewRootImpl 去实现的,而 WindwoManagerService 并不关心具体的 UI 内容,而是关心 Window 的层级,大小等信息。

2.2 Window 的删除

window 的删除与添加类似,也是通过 WindowManagerGlobal 来实现的,来看看 removeView方法:

    @UnsupportedAppUsage    public void removeView(View view, boolean immediate) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        synchronized (mLock) {            int index = findViewLocked(view, true);            View curView = mRoots.get(index).getView();            removeViewLocked(index, immediate);            if (curView == view) {                return;            }            throw new IllegalStateException("Calling with view " + view                    + " but the ViewAncestor is attached to " + curView);        }    }

当 view 不为null,则通过 findViewLocked() 找到下标,然后通过 mRoots 找到当前的 view,并通过 removeViewLocked() 方来实现删除:

    private void removeViewLocked(int index, boolean immediate) {        ViewRootImpl root = mRoots.get(index);        View view = root.getView();        if (view != null) {            InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);            if (imm != null) {                imm.windowDismissed(mViews.get(index).getWindowToken());            }        }        boolean deferred = root.die(immediate);        if (view != null) {            view.assignParent(null);            if (deferred) {                mDyingViews.add(view);            }        }    }

它主要做以下几个动作:

  1. 如果能找到view,关闭软键盘
  2. 通过 root.die(immediate) 来进行同步删除,或者异步删除,这个后面一起分析
  3. 通过 mDyingViews 添加要移除的view

上面说到 root.die(immediate) 删除是同步还是异步的,它的 die() 方法如下:

    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();            return false;        }        if (!mIsDrawing) {            destroyHardwareRenderer();        } else {            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());        }        mHandler.sendEmptyMessage(MSG_DIE);        return true;    }

可以看到,如果是同步的,直接通过 doDie() 方法去删除,如果是异步的,则通过 mHandler.sendEmptyMessage(MSG_DIE); 发送一个消息,在handleMessage 也是执行 doDIe() 方法;

doDie() 的实现是怎么样的呢?如下:

    void doDie() {        checkThread();        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);        synchronized (this) {            if (mRemoved) {                return;            }            mRemoved = true;            if (mAdded) {                dispatchDetachedFromWindow();            }            if (mAdded && !mFirst) {                destroyHardwareRenderer();                if (mView != null) {                    int viewVisibility = mView.getVisibility();                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;                    if (mWindowAttributesChanged || viewVisibilityChanged) {                        // If layout params have been changed, first give them                        // to the window manager to make sure it has the correct                        // animation info.                        try {                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {                                mWindowSession.finishDrawing(mWindow);                            }                        } catch (RemoteException e) {                        }                    }                    destroySurface();                }            }            mAdded = false;        }        WindowManagerGlobal.getInstance().doRemoveView(this);    }

如果有被添加,调用dispatchDetachedFromWindow() ,它是实现 view 删除的主要功能:

  1. mView.dispatchDetachedFromWindow(),从而回调 View 的 onDetachedFromWindow() 方法;
  2. 删除硬件渲染、清除回调,销毁 surface
  3. 通过 mWindowSession.remove(mWindow),从 WindowManagerService 删除 window。

然后通过 WindowManagerGlobal.getInstance().doRemoveView(this) 从 mRoots 等删除这个 view 的索引:

    void doRemoveView(ViewRootImpl root) {        synchronized (mLock) {            final int index = mRoots.indexOf(root);            if (index >= 0) {                mRoots.remove(index);                mParams.remove(index);                final View view = mViews.remove(index);                mDyingViews.remove(view);            }        }        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {            doTrimForeground();        }    }

2.3 Window 的更新

Window 的更新也是通过 WindowmanagerGlobal ,来看看updateViewLayout():

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        if (!(params instanceof WindowManager.LayoutParams)) {            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");        }        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;        view.setLayoutParams(wparams);        synchronized (mLock) {            int index = findViewLocked(view, true);            ViewRootImpl root = mRoots.get(index);            mParams.remove(index);            mParams.add(index, wparams);            root.setLayoutParams(wparams, false);        }    }

首先,会通过 view.setLayoutParams(wparams); 更新 view 的 params ,接着会通过 root.setLayoutParams(wparams, false) 方法,ViewRootImpl 会通过 scheduleTraversals() 重新对 view 进行 测量,布局,绘制的工作。

这样,我们就学习完 window 与 View,ViewRootImpl 等关系了。

参考Android艺术开发探索

更多相关文章

  1. Android(安卓)Studio 文件提前结束
  2. 关于Android实现滑动返回的几种方法总结
  3. WebView用法与JS交互
  4. Android(安卓)中音频视频开发
  5. Android中Service服务详解(二)
  6. 安卓 每日一题 2020年5月、6月
  7. android 兼容API的检查
  8. 【Android】下拉刷新实现
  9. android AsyncTask使用总结

随机推荐

  1. jtable表头渲染器
  2. 无法从其他用户的crontab作业执行java程
  3. Visual Studio 2012本身都是已经支持Jque
  4. Web分页实现及实例演示(一)——subList()
  5. 程序主菜单之javascript完全模拟
  6. 再论javaIO之拷贝MP3(read方法返回int的原
  7. Java的Grizzly为缓冲区占用了大量内存?
  8. Kotlin:相当于KClass的getClass()。
  9. 常用设计模式学习
  10. 关于Hadoop查看进程时jps命令出现Error: