Android Framework之WMS

702 阅读7分钟

Android Framework之WMS

WMS的定义

它是framework层的窗口管理服务,职责是管理android系统中所有的window。其中包含了添加窗口、删除窗口、token管理、输入法管理、系统事件消息收集和分发、活动窗口管理(FocusWindow)、活动应用管理(FocusApp)、转场动画。

Acitvity和Window

我们都知道activity和service最大的区别是一个有图像显出,一个是没有图像显示的。activity之所有能有图像显示,就是因为它包含了一个window窗口,而service没有window,所以它只能在后台运行,无法进行图像展示。

window对我们开发来说是一个展示图像的窗口,对用户来说就是一个界面。从surfaceFlinger的角度来看,它是一个layer图层,承载和界面相关的数据和属性。从WMS来说,它是一个WindowState,用于管理界面相关的数据。

一个actiivty中只有一个window,前者控制生命周期和事件处理,后者只进行视图控制。

一个系统中有很多应用,一个应用有很多activity,而所有应用中关于Acitivity的管理,都是由AMS来进行的,但ams值负责activity,不负责window窗口管理,所以就延伸出了WindowManagerService(WMS),专门用来进行Window的管理,控制window显示隐藏以及显示的位置。

WMS和AMS都是由SystemSerer(SM)来进行启动的,也都运行在一个进程中。

对Window的基础认识:

  • 它是窗口,也是所有View的管理者,任何视图都需要通过window来进行呈现,事件传递是Window(PhoneWindow)->DecorView->View
  • window是抽象类,具体的代码实现是在PhoneWindow
  • 创建window通过WindowManager创建
  • Window和WMS的通信方式是Binder,使用AIDL接口来实现的。
  • 每个Window都需要指定Type,应用窗口type值是【0-99】、应用子窗口type值是【1000-1999】、系统窗口【2000-2999】。具体对应值在WindowManager.LayoutParams类中。我们熟悉的toast type类型就是2005,系统键盘是2004,系统弹窗是2003,而悬浮窗也是系统弹窗,type值是2038。我们常见的activity type类型就是2。

WMS相关类功能描述。

  • WMS:管理窗口的创建、更新、删除、显示顺序等,是WindowManager的实现类。
  • Window: 手机上一块显示区域,添加window的过程就是申请分配一个surface的过程。
  • WindowManager: 应用和window之间的管理接口,管理窗口的顺序、消息等。
  • PhoneWindowManager:实现、定义窗口的各种策略。如:Home按键、Back按键是在此处理的,帮助wms矫正一些不合理的窗口属性。
  • Token:它是窗口令牌,本质是一个binder,用于窗口的身份验证,如你使用WindowManager.addView的时候将type类型设置成系统值或者toast值,就会报错,它报错的判断依据就是使用token来效验。
  • Seeion: 会话,一个app中只有一个Seeion,应用也是通过session和WMS进行通信。
  • WindowState:在WMS中保存、记录Window的类,和ActivityRecord一样,记录Activity的作用。
  • DisplayContent:代表屏幕,一个android设备可以有多个屏幕(车载上很常见)
  • WindowAnimator:管理、渲染window动画的类
  • Choreographer:用于控制窗口动画、屏幕旋转等操作。它拥有系统同步事件的能力,所以可以在合适的时机通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致的画面撕裂。
  • SurfaceFlinger:负责管理android系统的帧缓冲区(Frame Buffer),android设备的显示屏被抽象为一个帧缓存区,SurfaceFlinger服务通过向这个帧缓冲区写入内容,从而绘制出应用程序的用户界面。
  • InputManager:管理窗口事件通道,向通道上派发事件,它是InputManagerService的实例。
  • WindowContainer:管理window显示的次序,窗口大小。WindowToken、DisplayContent、WindowState都是它的子类。

屏幕刷新机制

我们肉眼能够分辨的的帧率是1秒24帧,只要在每秒中连续的输出不低于24帧,我们就不会感到卡顿。而android手机最常见的都是60HZ的刷新率,苹果手机是120HZ刷新率,目前android高端手机90hz和120hz的也有很多。

刷新时间16.67间隔的计算方式是:1000/60=16.67,所以我们手机刷新的间隔是16.67秒。

当然手机刷新率不是越高越好,它是和手机其他硬件性能相关的,如果屏幕刷新率很高,那他的刷新间隔就越短,如120hz的分辨率,刷新时间间隔为1000/120=8.3秒,在这么短的时间内进行刷新view,如果硬件性能不好,处理不过来,那很有可能丢帧,在连续时间段内持续丢帧,就会形成肉眼可见的卡顿。(持续时间段内掉帧,用户才会感到卡顿)

Window视图显示结构

网上找的图: image.png

图形显示数据传输流程如下:

view --> canvas --> surface --> surfaceFlinger --> 图像引擎库(OpenGL、Skia) --> Graphic Buffer--> FrameBuffer --> 屏幕绘制

WMS启动时序

WMS和AMS一样,都出通过SystemService来进行启动的。

SystemService-->startOtherService()

    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
            new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);

WindowManagerService-->main()

image.png

WMS的创建过程伴随着线程切换的过程。线程切换如下:

   SystemServer线程 --> DisplayThread线程

在android中,普通的handle.post 是异步机制的的(发送消息后,继续执行下面的代码,然后在onHandlerMessage中收到消息后再去执行其他代码)。

而上面main方法中使用runWithScissors却是同步的,先执行消息中的代码,然后再执行handler.post之后的代码。

runWithScissors实现同步的原理是线程锁定,阻塞等待handler执行完成。代码如下:

    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
        if (r == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout must be non-negative");
        }

        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }

        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }
    

BlockingRunnable中的Run方法:

    @Override
    public void run() {
        try {
            mTask.run();
        } finally {
            synchronized (this) {
                mDone = true;
                //唤醒下面的wait
                notifyAll();
            }
        }
    }
    
    

postAndWait方法代码如下:

    public boolean postAndWait(Handler handler, long timeout) {
        if (!handler.post(this)) {
            //消息发送失败直接返回false,不进行锁定
            return false;
        }
        
        
        synchronized (this) {
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() + timeout;
                //开启循环,
                while (!mDone) {
                    long delay = expirationTime - SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        //等待,上面执行完成之后会进行notifyAll唤醒
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                while (!mDone) {
                    try {
                       //等待,上面执行完成之后会进行notifyAll唤醒
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }

WMS启动完成之后在SystemServer中继续往下执行,将WMS和AMS绑定。

mActivityManagerService.setWindowManager(wm);

调用WMS中的onInitReady接口。

wm.onInitReady();--> initPolicy()-->UIThread.getHandler.runWithScissors()

继续往下,调用displayReady方法,初始化显示尺寸信息。

    try {
        wm.displayReady();
    } catch (Throwable e) {
        reportWtf("making display ready", e);
    }

继续往下:调用wms中的systemReady方法。

try {
    wm.systemReady();
} catch (Throwable e) {
    reportWtf("making Window Manager Service ready", e);
}

WMS流程总结

以上就是wms的启动流程,总结一下流程:

  • SystemServer.main()
  • SystemServer.startOtherService()
  • WindowManagerService-->main() DisplayThread线程创建
  • WindowManagerService-->initPolicy() UI线程进行初始化策略,和用户看到的界面显示关联,所以使用UI线程。
  • WindowManagerService-->displayReady()
  • WindowManagerService-->systemReady()

WMS的启动涉及三个线程:SystemServer线程、DisplayThread线程,UIThread。

其中创建WMS的线程是在DisplayThread线程中进行的。因为SystemServer负责的管理、启动的服务很多,而wms是管理所有窗口、负责显示相关的服务,也是用户能够明显感知到的,所以不太适合放在systemServer线程当中去初始化,因此才从使用DisplayThread线程去进行创建WMS。

WMS启动之后和Activity的关联。

WMS启动之后是如何和Activity关联起来的,我们继续看源码。

想要弄清楚他们是如何关联起来的,我们应该看activity的启动流程,前面的文章已经做了梳理,说以可以直接找到Activity创建和绑定的代码位置:

  • ActivityThread--> handleLaunchActivity()
  • ActivityThread--> performLaunchActivity()
  • Instrumentation --> newActivity()
  • Activity --> attach()

在attach方法中,做了如下事情:

//个Activity中创建一个PhoneWindow对象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//给刚刚创建的PhoneWindow对象设置WM;获取WindowManager接口类,WindowManagerImpl是它的实现类。
mWindow.setWindowManager(  (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  • Window --> setWindowManager();
  • WindowManagerImpl --> createLocalWindowManager(); 创建一个WindowManagerImpl对象。
  • WindowManagerGlobal --> getInstance();

addView() 流程

和Activity关联之后,我们最后再看一下AddView流程,我们的setContentView最后就是走的addView流程。

  • Actiivty -->setVisible --> makeVisible();activity显示的时候调用。

  • WindowManagerImpl --> addView();

  • WindowManagerGlobal --> addView();

  • ViewRootImpl --> setView(); 在此方法中,调用了WindowSession中的addToDisplayAsUser方法。

  • IWindowSession --> addToDisplayAsUser(); 此类是aidl接口,在Seesion中实现了此接口。

  • Seesion --> addToDisplayAsUser();

  • WindowManagerService --> addWindow();

    //addWindow方法中关键代码:
    //创建DisplayContent
    final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
    
      //创建WindowState
      final WindowState win = new WindowState(this, session, client, token, parentWindow,
              appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
              session.mCanAddInternalSystemWindow);
              
     
      win.attach();
    
  • WindowState --> attach();

  • Session --> windowAddedLocked();

  • SurfaceSession --> SurfaceSession() --> nativeCreate();

  • android_view_SurfaceSession.cpp --> nativeCreate();

      static jlong nativeCreate(JNIEnv* env, jclass clazz) {
          SurfaceComposerClient* client = new SurfaceComposerClient();
          client->incStrong((void*)nativeCreate);
          return reinterpret_cast<jlong>(client);
      }
      
    

最后

这里做了WMS几个重要的流程,包括wms启动、和activity关联、addView。wms中还有其他很多重要的东西没有总结,后面有时间补充。

本文是自己的学习记录,如有错误,感谢指出。