Android控件系统(二)——Framework概述

268 阅读7分钟

Framework框架

  Framework定义了客户端组件和服务器端组件功能及接口,包含3个部分:服务端、客户端和linux驱动,如下图所示:

image

  Framework中所包含的服务是非常多的,为了清晰的描述服务端、客户端和linux驱动三者的关系,我们只是描述了它们其中的一小部分,这一小部分也是为后续讲解窗口的创建、Activity启动、绘制、事件分发服务的。

服务端

  服务端主要包含两个重要类,分别是WindowManagerService(WMS)和ActivityManagerService(AMS):

  • WMS:作用是为所有的应用程序分配窗口,并管理这些窗口。包括分配窗口的大小、叠放个窗口的叠放次序、隐藏或显示窗口;
  • AMS:作用是管理所有应用程序中的Activity;

  除此之外,在服务端还包括2个信息处理类:

  • KeyQ:该类为wms的内部类,继承于keyinputQueue类,keyQ对象一旦创建,就立即启动一个线程,该线程会不断地读取用户的ui操作消息,比如按键、触摸屏、trackball、鼠标等。并把这些消息放到一个消息队列QueueEvent类中;
  • InputDispatcherThread:该类的对象一旦创建,也就立即启动一个线程,该线程会不断地从QueueEvent中取出用户消息,并进行一定的过滤,用wms中的相应函数来做过滤处理,过滤后,再将这些消息发给当前活动的客户端程序中。

客户端

客户端主要包括以下重要类:

  • ActivityThread类
    该类为应用程序的主线程类,所有的APK程序有且只有一个ActivityThread类,程序的入口为该类中的static main()函数,ActivityThread所在的线程即为ui线程或主线程。

  • Activity类
    该类为APK程序的一个最小运行单位,一个apk程序中可以包含多个activity对象,activityThread主类会根据用户操作选择运行哪个activity对象。

  • PhoneWindow类
    该类继承与Window类,同时phonewindow类内部包含了一个decorView对象,简而言之,PhoneWindow是把一个FrameLayout进行了一定的包装,并提供了一组通用的窗口操作接口。

  • Window类
    该类是一组通用的窗口window操作api,这里的窗口仅仅是程序层面上的,wms所管理的窗口并不是window类,而是一个view或者viewgroup类,一般就是指DecorView类,即一个D而从容View就是wms所有管理的一个窗口,window是一个abstact类型。

  • DecorView类
    该类是一个FrameLayout的子类,并且是PhoneWindow的一个内部类,DecorView就是对普通的FrameLayout进行了一定的修饰,比如添加一个通用的TitleBar,并响应特定的按钮消息等。

  • ViewRoot类
    Wms管理客户端窗口时,需要通知客户端进行某种操作,这些都是通过IPC调用完成的,而在客户端窗口收到IPC调用后,都会把该调用转换为本地的一个异步调用,实现方式就是使用handler,ViewRoot就是继承与handler,其主要作用就是就接手wms的IPC调用转换为本地的一个异步调用。

  • w类
    该类继承于Binder,并且是一个viewRoot的一个内部类。WMS通知客户端窗口时,是通过IPC调用,也就是调用该Binder类,然后该Binder内部的处理函数一般会给该类所在的ViewRoot类发送一个Handler消息,以便进行异常处理。

  • windowManager类
    客户端要申请创建一个窗口,而具体创建窗口的任务是有wms完成的,windowManager类就像是一个部门经理,谁有什么需求就告诉他,由它和wms进行交互,客户端不能直接和wms进行交互。

Linux驱动

  Linux驱动和Framework相关的主要包含两个部分,分别是surfaceFlingger和Binder,每一个窗口都对应一个Surface,sf驱动的作用就是把各个surface显示在同一个屏幕上。
  Binder驱动的作用是提供框进程 ipc 的消息传递机制。

APK程序的运行过程

下图是一个更加完成的APK程序工作的流图:

image
  首先,ActivityThread从main()函数中开始执行,调用prepareMainLooper()为UI线程创建一个消息队列(MessageQueue)。

  然后创建一个ActivityThread对象,在ActivityThread的初始化代码中会创建一个H(Handler)对象和一个ApplicationThread(Binder)对象。其中Binder负责接收远程AmS的IPC调用,接收到调用后,则通过Handler把消息发送到消息队列,UI主线程会异步地从消息队列中取出消息并执行相应操作,比如start、stop、pause等。

  接着UI主线程调用Looper.loop()方法进入消息循环体,进入后就会不断地从消息队列中读取并处理消息。

  当ActivityThread接收到AmS发送start某个Activity后,就会创建指定的Activity对象。Activity又会创建PhoneWindow类→DecorView类→创建相应的View或者ViewGroup。创建完成后,Activity需要把创建好的界面显示到屏幕上,于是调用WindowManager类,后者于是创建一个ViewRoot对象,该对象实际上创建了ViewRoot类和W类,创建ViewRoot对象后,WindowManager再调用WmS提供的远程接口完成添加一个窗口并显示到屏幕上。

  接下来,用户开始在程序界面上操作。KeyQ线程不断把用户消息存储到QueueEvent队列中,InputDispatcherThread线程逐个取出消息,然后调用WmS中的相应函数处理该消息。当WmS发现该消息属于客户端某个窗口时,就会调用相应窗口的W接口。

  W类是一个Binder,负责接收Wms的IPC调用,并把调用消息传递给ViewRoot,ViewRoot再把消息传递给UI线程ActivityThread,ActivityThread解析该消息并做相应的处理。在客户端程序中,首先处理消息的是DecorView,如果DecorView不想处理某个消息,则可以将该消息传递给其内部包含的子View或ViewGroup,如果还没有处理,则传递给PhoneWindow,最后再传递给Activity。

客户端中的线程

在多任务操作系统中,任何程序都运行在线程之中。系统首先会为客户端程序分配一个线程,然后该线程从程序的入口处开始执行。那么,请思考以下问题。

  • Android APK程序中都有哪些线程?
  • 什么是UI线程?
  • 程序中自定义Thread和UI线程的区别是什么?

  首先,很明确地讲,包含有Activity的客户端程序至少包含三个线程,如图6-1所示。每个Binder对象都对应一个线程,Activity启动后会创建一个ViewRoot.W对象,同时ActivityThread会创建一个ApplicationThread对象,这两个对象都继承于Binder,因此会启动两个线程,负责接收Linux Binder驱动发送IPC调用。最后一个主要线程也就是程序本身所在的线程,也叫做用户交互(UI)线程,因为所有的处理用户消息,以及绘制界面的工作都在该线程中完成。

  自定义Thread和UI线程的区别在于,UI线程是从ActivityThread运行的,在该类中的main()方法中,已经使用Looper.prepareMainLooper()为该线程添加了Looper对象,即已经为该线程创建了消息队列(MessageQueue),因此,程序员才可以在Activity中定义Handler对象(因为声明Handler对象时,所在的线程必须已经创建了MessageQueue)。而普通的自定义Thread是一个裸线程,因此,不能直接在Thread中定义Handler对象,从使用场景的角度讲,即不能直接给Thread对象发消息,但是却可以给UI线程发消息。