android 窗口管理机制

1、简介

Android的窗口管理主要由Window, WindowManager, WindowManagerServic三者实现window是一个抽象的概念,它必须为其添加一个View实体才能被我们所看到,不管是Activity,Toast,还是其他的Dialog之类的组件,都必须放在window上。而如果我们要对window进行操作的话,就必须通过windowManager,事实上,对window的操作是通过WindowManagerServic来实现的,而WindowManager可以看做是一个管理者,而不是一个实施者,它提供各种方法和指令来操作window,但是这些操作指令都需要通过IPC传递给WindowManagerServic来具体实现。

2、Window

2.1 窗口类型

window有三种类型,从低到高分别为子window,应用window,系统window,每个window有着对应的层级(z-ordered), 层级大的可以覆盖在层级小的window上面,不同类型的window的层级范围如图所示

类型层级范围
子window0-99
应用window1000-1999
系统window2000-2999

在WindowManager.LayoutParams.TYPE_***中,我们可以看到多种不同的类型以及它们对应的值,系统运行期间,很有可能多个窗口属于同一个类型。因此WMS需要根据实际情况来调整他们的层级值。系统类的window创建必须要在AndroidManifest,xml中声明权限后才能创建。

2.2 窗口属性

通过设置不同的窗口属性,我们可以使UI窗口显示出不同的样式,它们就行窗口用户和WMS之间的协议一样,WMS负责实现用户端声明的这些属性。这些属性都放在了WindowManager.LayoutParams中,这里列举两个。

		// 只要这个window对用户是可见的,则允许在屏幕开启的时候锁定屏幕
        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;

		// 窗口后面的所有东西都变暗
        public static final int FLAG_DIM_BEHIND        = 0x00000002;

		// 此窗口不获得输入焦点,事件将分发给窗口后面的其他窗口
        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
        ......

比如有时候希望全屏显示,取消statusBar这些组件,就可以使用

        Window w = this.getWindow();
        w.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

window可以看做是一个容器,但它其实是一个抽象的概念,Activity,Dialog以及它们的视图都必须放在window上才能显现, 每个window对应着一个View和一个ViewRootImpl, 视图View负责定义window的布局以及样式等,而
ViewRootImpl则是负责View的渲染。window和View是通过ViewRootImpl来建立起联系的。

3、WindowManager

WindowManager是联系window和WindowManagerService的桥梁,是Android窗口管理机制的枢纽。它继承子ViewManager,定义了对于window的基本操作。

public interface WindowManager extends ViewManager{
.............
}
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

这样看起来就清楚多了,它其实主要就是对外提供了对View的增加,更新,删除操作。所以我们也可以直接通过WindowManager来对window实现这些操作。

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // setContentView(R.layout.mainactivity);
        // 添加一个文本框
        TextView textView=new TextView(this);
        textView.setText("Window text");
        // 设置布局参数
        WindowManager.LayoutParams layoutParams=new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                0,0,
                PixelFormat.TRANSPARENT
        );
        // 设置window的属性
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        // 设置window的层级
        layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

        layoutParams.gravity = Gravity.CENTER;
        WindowManager windowManager = getWindowManager();
        // 添加View
        windowManager.addView(textView,layoutParams);
    }

这里我们添加了一个文本框,并且设置它的层级的TYPE_APPLICATION_OVERLAY,表示可以浮在其他应用的上面,具体值是2038,即一个系统window。除了要声明权限之外,我们还要在应用/高级设置中开启它可以浮在其他应用上面的权限才行。

<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

在这里插入图片描述
最后,可以发现这个textview可以显示在应用中,也可以直接显示在桌面上。这里说明,即便我们退出了MainActivity之后,这个textview依然存在,即textview真正依附的是window而不是Activity,对于窗口显示来说,Activity其实不是必须的,比如经常使用的Toast,它就没有对应的Activity。
在这里插入图片描述

那么Activity是如何创建window,以及绑定对应的windowManager的呢?下面我们来探究一下。在Activity调用onCreate之前,它会先调用attach方法

    final void attach(Context context, ActivityThread aThread,
		......
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
		
		......

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
        mWindow.setPreferMinimalPostProcessing(
                (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);

        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }

在这个方法中我们可以看到,window的具体实现类是PhoneWindow,将其实例化之后传递给Activity的成员mWindow,之后设置好它的回调函数。在方法mWindow.setWindowManager()中,将window和windowManager绑定在一起。下面看一下setWindowManager()方法


public void setWindowManager(WindowManager wm, IBinder appToken, String appName,

            boolean hardwareAccelerated) {

    ...

        if (wm == null) {

            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

        }

        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

    }

这里可以看到,如果wm是空的话,那么为其赋值,这里其实是获取系统服务
WindowManagerService,然后在调用createLocalWindowManager来创建一个真正的windowManager代理,再进入createLocalWindowManager方法中


public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

        return new WindowManagerImpl(mContext, parentWindow);

    }


发现它其实是返回了一个WindowManagerImp的实例。综上,attach方法其实完成的主要工作就是创建了Activity的window对象和windowManager对象,而将Activity对应的View附属到window中,其实是在onCreate方法中完成的,即 setContentView()方法。

public void setContentView(int layoutResID){
  getWindow().setContentView(layoutResID);
   ...
}

这里通过getWindow获取之前创建的PhoneWindow对象,然后再调用setContentView将window与View绑定在一起。

在Activity调用onResume方法之后,会调用makeVisible()方法,将window加入到windowManager中,那么windowManager就可以对window进行管理了。


void makeVisible() {

        if (!mWindowAdded) {

            ViewManager wm = getWindowManager();

            wm.addView(mDecor, getWindow().getAttributes());

            mWindowAdded = true;

        }

        mDecor.setVisibility(View.VISIBLE);

    }

之前我们说道过,window是抽象的,必须通过windowManager对其进行访问,windowManager是继承自viewmanager的,它提供了对视图的增,删,改操作。而视图是与window绑定在一起的,所以对View的操作其实也是对window的操作。
上面代码中可以看出,windowManager的具体实现类其实是WindowManagerImpl,看一下它的源码


@Override

        public void addView(View view, ViewGroup.LayoutParams params){

        ...

            mGlobal.addView(view, params, mDisplay, mParentWindow);

        }

        @Override

        public void updateViewLayout(View view, ViewGroup.LayoutParams params){

        ...

            mGlobal.updateViewLayout(view, params);

        }

        @Override

        public void removeView(View view){

        ...

            mGlobal.removeView(view, false);

        }


它把对视图的操作都交给了成员mGlobal去实现,mGlobal是一个WindowManagerGlobal类,再看一下它的源码,mGlobal中提供了对视图进行增、删、改的操作函数,同时还用了几个集合来保存和window相关的信息,具体如下。

public final class WindowManagerGlobal{
	// 存储所有window对应的View
    private final ArrayList<View> mViews = new ArrayList<View>();
	// 存储所有window对应的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    // 存储所有的window对应的布局参数 
    private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();
     //  存储已经调用removeView方法但是操作删除还未完成的对象
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

	public void addView(...){
		...
	}
	public void updateViewLayout(...){
		...
	}
	public void removeView(...){
		...
	}
	...
}

综上,WindowManager到WindowManagerService的调用路径为
在这里插入图片描述

4、WindowManagerService

WindowManagerService是实际控制window的显示以及渲染等操作的,它的实现比较复杂,由SystemService负责启动的,它就像一个导演一样,和surfaceFlinger一起负责界面的显示工作,全局的事件派发工作,只有在关机的时候才能退出。

4.1 服务启动

和AMS等服务一样,WMS也是系统服务的一部分,它由SystemServer负责启动,并加到服务列表中,WMS提供了一个静态的main方法来实现真正的创建工作。

// SystemServer.java 
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
            // 创建真正的WMS对象
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

4.2 基本功能

在文件IWindowManager.aidl中,我们可以看到WindowManagerService可以提供哪些功能

......
    void getBaseDisplaySize(int displayId, out Point size);
    void setForcedDisplaySize(int displayId, int width, int height);
    void clearForcedDisplaySize(int displayId);
    @UnsupportedAppUsage
    int getInitialDisplayDensity(int displayId);
    int getBaseDisplayDensity(int displayId);
    void setForcedDisplayDensityForUser(int displayId, int density, int userId);
    void clearForcedDisplayDensityForUser(int displayId, int userId);
    void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable

    void setOverscan(int displayId, int left, int top, int right, int bottom);

    // These can only be called when holding the MANAGE_APP_TOKENS permission.
    void setEventDispatching(boolean enabled);
    void addWindowToken(IBinder token, int type, int displayId);
    void removeWindowToken(IBinder token, int displayId);
    void setFocusedApp(IBinder token, boolean moveFocusNow);
    void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
    ......

从这个文件中可以看到,WMS的功能庞杂繁多,比如获取显示器的信息,截取屏幕,锁定屏幕等各种功能。

4.3 工作方式

WMS的工作线程是SystemServer中的wmHandlerThread, 这样的设计使得SystemServer中的其他服务可以直接将事件投递到wmHandlerThread线程中,从而WMS可以直接从线程中获取事件进行处理,此外,服务请求也可以直接通过Binder机制调用WMS进行服务,所以调用者可以通过直接或者间接的方式调用到WMS服务。

  • 事件投递
  • 直接调用

在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值