Android应用窗口Window

225 阅读3分钟

窗口管理框架

从上图可以看出,Android的显示系统分为3层:

  • UI框架层:负责管理窗口中View组件的布局与绘制以及响应用户输入事件
  • WindowManagerService层:负责管理窗口Surface的布局与次序
  • SurfaceFlinger层:将WindowManagerService管理的窗口按照一定的次序显示在屏幕上

在Android显示框架里有这么几个角色:

  • Activity:应用视图的容器。
  • Window:应用窗口的抽象表示,它的实际表现是View。
  • View:实际显示的应用视图。
  • WindowManagerService:用来创建、管理和销毁Window。

窗口类型

Window 在Android中是一种窗口的概念,他是View的管理者,而WindowManager则是系统Window的管理者

Window在Android中有三种类型:

z-index 是系统Window的层级概念,z-index越大,窗口越位于顶层

  • 应用窗口,z-index 在0~99之间
  • 子Window,z-index 在1000~1999,不能独立存在,依附于父系统Window,如Dialog
  • 系统系统Window,z-index在2000-2999,系统Window,它往往需要声明权限才能创建,如Toast,状态栏,错误提示框

窗口的实现

Window是一个抽象类,它的唯一实现类是PhoneWindow,PhoneWindow里包含了以下内容:

  • private DecorView mDecor:DecorView是Activity中的顶级View,它本质上是一个FrameLayout,一般说来它内部包含标题栏和内容栏(com.android.internal.R.id.content)。
  • ViewGroup mContentParent:窗口内容视图,它是mDecor本身或者是它的子View。
  • private ImageView mLeftIconView:左上角图标
  • private ImageView mRightIconView:右上角图标
  • private ProgressBar mCircularProgressBar:圆形loading条
  • private ProgressBar mHorizontalProgressBar:水平loading条
  • 其他的一些和转场动画相关的Transition与listener

setContentView()

我们通常是在Activity里进行调用,但是它的实际实现是在PhoneWindow里。

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    
    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            //1. 如果没有DecorView则创建它,并将创建好的DecorView赋值给mContentParent
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //2. 将Activity传入的布局文件生成View并添加到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            //3. 回调Window.Callback里的onContentChanged()方法,这个Callback也被Activity
            //所持有,因此它实际回调的是Activity里的onContentChanged()方法,通知Activity
            //视图已经发生改变。
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }    
}

这个方法主要做了两件事情:

  1. 如果没有DecorView则创建它,并将创建好的DecorView赋值给mContentParent
  2. 将Activity传入的布局文件生成View并添加到mContentParent中
  3. 回调Window.Callback里的onContentChanged()方法,这个Callback也被Activity所持有,因此它实际回调的是Activity里的onContentChanged()方法,通知Activity视图已经发生改变。

DecorView已经被创建并初始化完毕,Activity里的布局文件也被成功的添加到PhoneWindow的mContentParent(实际上就是DecorView,它是Activity的顶层View) 中,但是这个时候DecorView还没有真正的被WindowManager添加到Window中,它还无法接受用户的输入信息和焦点事件,这个时候就相当于走到了Activity的onCreate()流程,界面还 未展示给用户。
直到走到Activity的onResume()方法,它会调用Activity的makeVisible()方法,DecorView才真正的被用户所看到。

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {
    /**
     * Control whether this activity's main window is visible.  This is intended
     * only for the special case of an activity that is not going to show a
     * UI itself, but can't just finish prior to onResume() because it needs
     * to wait for a service binding or such.  Setting this to false allows
     * you to prevent your UI from being shown during that time.
     *
     * <p>The default value for this is taken from the
     * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme.
     */
    public void setVisible(boolean visible) {
        if (mVisibleFromClient != visible) {
            mVisibleFromClient = visible;
            if (mVisibleFromServer) {
                if (visible) makeVisible();
                else mDecor.setVisibility(View.INVISIBLE);
            }
        }
    }
    
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
}

还有setContentView是替换View,addContentView是添加View。实现原理相同。