Jimmy Chen

A Programmer

(转载)Android开机动画流程简述

  在详细分析SurfaceFlinger模块之前要先看看Android的开机动画,因为这个BootAnimation是一个C++应用程序,需要使用SurfaceFlinger服务来创建和渲染自己的Surface,并且不涉及与用户进行交互,所以能以最简洁的方式体现Android与SurfaceFlinger服务的关系。

开机动画的启动

  第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。第三个开机画面BootAnimation是在系统服务启动的过程中出现的,它是一个动态的画面。无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。接下来,我们就分别分析第三个画面是如何在fb上显示的。

bootanimation的启动过程

  第三个开机动画使用应用程序BootAnimation来负责显示的,它在启动脚本init.rc中被配置成了一个服务,位于system/core/rootdir/Init.rc:

  应用程序bootanimation的用户和用户组名称分别被设置为graphics。注意, 用来启动应用程序bootanimation的服务是disable的,即init进程在启动的时候,不会主动将应用程序bootanimation启动起来。

  当SurfaceFlinger服务启动的时候,它会通过修改系统属性ctl.start的值来通知init进程启动应用程序bootanimation,以便可以显示第三个开机画面。

  在早期的Android版本中,SurfaceFlinger服务是由SystemServer启动的。但在Android5.0中,该服务是init进程启动过程中就启动了。在init.rc中能看到对该服务的描述:

  SurfaceFlinger服务的源码路径位于framework/native/services/surfaceflinger/下面,服务的入口在下面的main_surfaceflinger.cpp中,我们查看它的main函数:

  主要工作就是新建一个SurfaceFlinger对象,然后调用其中的init函数,最后调用run函数。我们的重点在init方法,位于framework/native/services/surfaceflinger/SurfaceFlinger.cpp中:

  最后一行调用了startBootAnim函数,我们继续往下看:

  这里讲系统属性”service.bootanim.exit”设置为”0”,并将”ctl.start”设置为”bootanim”。重点是第二个。

  当系统属性发生改变时,init进程就会接收到一个系统属性变化通知,这个通知最终是由在init进程中的函数handle_property_set_fd来处理的。我们可以查看init进程的入口main函数,位于system/core/init/Init.c中:

  可以看到,init进程会使用poll机制来轮询事件,其中一个事件是系统属性值被修改。得到该事件后,会执行handle_property_set_fd(),位于system/core/init/Property_service.c中:

  init进程是通过一个socket来接收系统属性变化事件的。每一个系统属性变化事件的内容都是通过一个prop_msg对象来描述的。在prop_msg对象对,成员变量name用来描述发生变化的系统属性的名称,而成员变量value用来描述发生变化的系统属性的值。系统属性分为两种类型,一种是普通类型的系统属性,另一种是控制类型的系统属性(属性名称以“ctl.”开头)。控制类型的系统属性在发生变化时,会触发init进程执行一个命令,而普通类型的系统属性就不具有这个特性。注意,改变系统属性是需要权限,因此,函数handle_property_set_fd在处理一个系统属性变化事件之前,首先会检查修改系统属性的进程是否具有相应的权限,这是通过调用函数check_control_perms或者check_perms来实现的。

  从前面的调用过程可以知道,当前发生变化的系统属性的名称为“ctl.start”,它的值被设置为“bootanim”。由于这是一个控制类型的系统属性,因此,在通过了权限检查之后,另外一个函数handle_control_message就会被调用,以便可以执行一个名称为“bootanim”的命令。

  函数handle_control_message实现在system/core/init/Init.c中,如下所示:

  控制类型的系统属性的名称是以”ctl.”开头,并且是以“start”或者“stop”结尾的,其中,“start”表示要启动某一个服务,而“stop”表示要停止某一个服务,它们是分别通过函数msg_start和msg_stop来实现的。由于当前发生变化的系统属性是以“start”来结尾的,因此,接下来就会调用函数msg_start来启动一个名称为“bootanim”的服务。

  函数msg_start实现在文件system/core/init/Init.c中,如下所示:

  该函数首先调用service_find_by_name(),从service_list中查询要启动的服务是否有存在,若存在,返回服务的相关信息。因为init.rc中有bootanimation的定义,因此在init进程执行parse_config()时,会将该服务添加到service_list中,所以bootanimation应用是存在的。然后,如果找到了该服务,就调用service_start启动服务。

  到此,bootanimation应用就启动了。

开机动画显示

  从前面的内容可以知道,名称等于“bootanim”的服务所对应的应用程序为/system/bin/bootanimation,这个应用程序实现在frameworks/base/cmds/bootanimation目录中,其中,应用程序入口函数main是实现在frameworks/base/cmds/bootanimation/Bootanimation_main.cpp中的,如下所示:

  这个函数首先检查系统属性“debug.sf.nobootnimaition”的值是否不等于0。如果不等于的话,那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象。这个BootAnimation对象就是用来显示第三个开机画面的。由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信,因此,应用程序bootanimation就需要启动一个Binder线程池。

  接着我们看看BootAnimation类的声明,位于frameworks/base/cmds/bootanimation/BootAnimation.h中:

  BootAnimation类继承了Thread类和IBinder::DeathRecipient类,其中几个重要的函数说明如下:

  • onFirstRef() —– 属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当有sp包装的类初始化的时候调用;
  • binderDied() —– 当对象死掉或者其他情况导致该Binder结束时,就会回调binderDied()方法;
  • readyToRun() —– Thread执行前的初始化工作;
  • threadLoop() —– 每个线程类都要实现的,在这里定义thread的执行内容。这个函数如果返回true,且没有requestExist()没有被调用,则该函数会再次执行;如果返回false,则threadloop中的内容仅仅执行一次,线程就会退出。

  其他主要函数的说明如下:

  • android() —– 显示系统默认的开机画面;
  • movie() —– 显示用户自定义的开机动画。

  BootAnimation类成员函数的实现位于frameworks/base/cmds/bootanimation/BootAnimation.cpp。

  BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用:

  mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信的,它是在BootAnimation类的构造函数中创建的,如下所示:

  SurfaceComposerClient类内部有一个实现了ISurfaceComposerClient接口的Binder代理对象mClient,这个Binder代理对象引用了SurfaceFlinger服务,SurfaceComposerClient类就是通过它来和SurfaceFlinger服务通信的。这个我们后面章节会详细分析。

  回到BootAnimation类的成员函数onFirstRef中,由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知,这是通过调用成员变量mSession的成员函数linkToComposerDeath来注册SurfaceFlinger服务的死亡接收通知来实现的。

  BootAnimation类继承了Thread类,因此,当BootAnimation类的成员函数onFirstRef调用了父类Thread的成员函数run之后,系统就会创建一个线程,这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作,后面再调用BootAnimation类的成员函数htreadLoop来显示第三个开机画面。

  BootAnimation类的成员函数readyToRun的实现如下所示:

readyToRun主要做了如下工作:

1.

  BootAnimation类的成员函数session用来返回BootAnimation类的成员变量mSession所描述的一个SurfaceComposerClient对象。通过调用SurfaceComposerClient对象mSession的成员函数createSurface可以获得一个SurfaceControl对象control。

  SurfaceComposerClient类的成员函数createSurface首先调用内部的Binder代理对象mClient(frameworks/native/services/surfaceflinger/Client.cpp)来请求SurfaceFlinger返回一个类型为(class Handle : public BBinder, public LayerCleaner)Binder代理对象(封装了SurfaceFlinger的sp指针和Layer对象)handle,和一个IGraphicBufferProducer的sp指针(封装了SurfaceFlinger的sp指针)gbp,接着再使用这两个对象来创建一个SurfaceControl对象。创建出来的SurfaceControl对象的成员变量handle就指向了从SurfaceFlinger返回来的类型为Handle 的Binder代理对象。有了这个Binder代理对象之后,SurfaceControl对象就可以和SurfaceFlinger服务通信了。(下一章节会分析)

  调用SurfaceControl对象control的成员函数getSurface会返回一个Surface对象s。这个Surface对象s内部也有一个类型为IGraphicBufferProducer的sp指针mGraphicBufferProducer,这个sp指针与前面所创建的SurfaceControl对象control的内部的sp指针(封装了SurfaceFlinger的sp指针)gbp引用的是同一个对象。这样,Surface对象s也可以通过其内部的sp指针mGraphicBufferProducer来和SurfaceFlinger服务通信。(下一章节会分析)

  Surface类继承了ANativeWindow类。ANativeWindow类是连接OpenGL和Android窗口系统的桥梁,即OpenGL需要通过ANativeWindow类来间接地操作Android窗口系统。这种桥梁关系是通过EGL库来建立的,所有以egl为前缀的函数名均为EGL库提供的接口。

2.

  为了能够在OpenGL和Android窗口系统之间的建立一个桥梁,我们需要一个EGLDisplay对象display,一个EGLConfig对象config,一个EGLSurface对象surface,以及一个EGLContext对象context,其中,EGLDisplay对象display用来描述一个EGL显示屏,EGLConfig对象config用来描述一个EGL帧缓冲区配置参数,EGLSurface对象surface用来描述一个EGL绘图表面,EGLContext对象context用来描述一个EGL绘图上下文(状态),它们是分别通过调用egl库函数eglGetDisplay、eglChooseConfig、eglCreateWindowSurface和eglCreateContext来获得的。注意,EGLConfig对象config、EGLSurface对象surface和EGLContext对象context都是用来描述EGLDisplay对象display的。有了这些对象之后,就可以调用函数eglMakeCurrent来设置当前EGL库所使用的绘图表面以及绘图上下文。

  还有另外一个地方需要注意的是,每一个EGLSurface对象surface有一个关联的ANativeWindow对象。这个ANativeWindow对象是通过函数eglCreateWindowSurface的第三个参数来指定的。在我们这个场景中,这个ANativeWindow对象正好对应于前面所创建的 Surface对象s。每当OpenGL需要绘图的时候,它就会找到前面所设置的绘图表面,即EGLSurface对象surface。有了EGLSurface对象surface之后,就可以找到与它关联的ANativeWindow对象,即Surface对象s。有了Surface对象s之后,就可以通过其内部的sp指针mGraphicBufferProducer来请求SurfaceFlinger服务返回帧缓冲区硬件设备的一个图形访问接口。这样,OpenGL最终就可以将要绘制的图形渲染到帧缓冲区硬件设备中去,即显示在实际屏幕上。屏幕的大小,即宽度和高度,可以通过函数eglQuerySurface来获得。

3.

  动画文件的读取是按顺序进行的,如果读取成功,则不再读取后续的文件,如果失败,则读取下一个文件。顺序如下:

  • 如果设备的加密功能已经开启,或者设备正在进行加密,则读取加密开机动画文件,路径为:

  • OEM厂商指定的开机动画,路径为:

  • 系统开机动画,路径为:

  这一步执行完成之后,用来显示第三个开机画面的线程的初始化工作就执行完成了,接下来,就会执行这个线程的主体函数,即BootAnimation类的成员函数threadLoop。

  BootAnimation类的成员函数threadLoop的实现如下所示:

  这个函数流程比较简单,首先判断自定义的开机动画文件mZip是否存在,如果存在就调用movie()完成自定义开机画面的显示;如果不存在,调用android()完成系统默认开机画面的显示。然后进行开机动画显示后的销毁、释放工作,主要就是readyToRun中初始化的一些EGL对象。最后终止线程,并return。注意,movie()和android()的返回值都是false,因此线程结束也会返回false。threadLoop()函数如果返回值为false,则该函数中的内容只会执行一次;如果返回true,则会不停的执行。这里返回false,因此只会执行一次。

  接下来,我们就分别分析BootAnimation类的成员函数android和movie的实现。

系统默认开机动画android()

  BootAnimation类的成员函数android的实现如下所示:

  一共有以下几个步骤:

  Android系统默认的开机动画是由两张图片android-logo-mask.png和android-logo-shine.png中。这两张图片保存在frameworks/base/core/res/assets/images目录中,它们最终会被编译在framework-res模块(frameworks/base/core/res)中,即编译在framework-res.apk文件中。编译在framework-res模块中的资源文件可以通过AssetManager类来访问。

《(转载)Android开机动画流程简述》

  BootAnimation类的成员函数android首先调用另外一个成员函数initTexture来将根据图片android-logo-mask.png和android-logo-shine.png的内容来分别创建两个纹理对象,这两个纹理对象就分别保存在BootAnimation类的成员变量mAndroid所描述的一个数组中。通过混合渲染这两个纹理对象,我们就可以得到一个开机动画,这是通过中间的while循环语句来实现的。

  图片android-logo-mask.png用作动画前景,它是一个镂空的“ANDROID”图像。图片android-logo-shine.png用作动画背景,它的中间包含有一个高亮的呈45度角的条纹。在每一次循环中,图片android-logo-shine.png被划分成左右两部分内容来显示。左右两个部分的图像宽度随着时间的推移而此消彼长,这样就可以使得图片android-logo-shine.png中间高亮的条纹好像在移动一样。另一方面,在每一次循环中,图片android-logo-mask.png都作为一个整体来渲染,而且它的位置是恒定不变的。由于它是一个镂空的“ANDROID”图像,因此,我们就可以通过它的镂空来看到它背后的图片android-logo-shine.png的条纹一闪一闪地划过。这个while循环语句会一直被执行,直到应用程序/system/bin/bootanimation被结束为止。

  在循环语句最后会执行checkExit()函数:

  首先调用property_get获取属性EXIT_PROP_NAME的值:

 &ems;然后判断该值,如果为1,则调用requestExit()要求退出当前线程,该函数是异步的。位于system/core/libutils/Thread.cpp中:

  回到android()代码:

  调用exitPending(),改函数判断requestExit()是否被调用过,如果调用过则返回true,否则为false。依然位于system/core/libutils/Thread.cpp中:

  这样,当属性“service.bootanim.exit”值被设为”1”时,android()就会调用requestExit(),exitPending()返回值为true。于是do…while()循环就会退出,开机动画绘制就会结束。

  至于什么时候是哪个服务将属性“service.bootanim.exit”的值设置为1的,我们后面讲开机动画的停止的时候会提到。

自定义开机动画movie()

  BootAnimation类的成员函数movie的实现比较长,我们分段来阅读:

Part.1:

  从前面BootAnimation类的成员函数readyToRun的实现可以知道,如果目标设备上存在压缩文件/system/media/bootanimation.zip(另外两种我们忽略),那么BootAnimation类的成员变量mZip就会指向它,这段代码作用是读取开机动画文件mZip中的描述文件“desc.txt”。每个动画文件压缩包中必须要包含一个desc.txt,该文件用来描述开机动画如何显示。下面以一个示例来分析一下该文件:

Part.2:

  第1行用来描述开机动画在屏幕显示的大小及速度。具体为:开机动画的宽度为480个像素,高度为640个像素,显示频率为每秒20帧,即每帧显示1/20秒。

  下面的每一行代表一个片段,显示的时候会按照顺序从上到下依次显示。第1个字符为片段类型,有’c’和’p’两种,两者的区别后面会结合代码说明。

  第2个数字为该片段重复显示的次数,如果为‘0’,表示会无限重复显示;第3个数字为两次显示之间的间隔,单位为第一行中定义的每帧显示的时间;第4个字符串为该片段所在的文件夹,一个片段可以由多个png图片组成,都存放在folder文件夹中。

  • “p 1 0 folder1”代表该片段显示1次,与下一个片段间隔0s,该片段的显示图片路径为bootanimation.zip/folder1。
  • “p 2 20 folder2”代表该片段显示2次,且两次之间显示的间隔为20(1/20)=1s,与下一个片段间隔20(1/20)=1s,该片段的显示图片路径为bootanimation.zip/folder2。
  • “c 0 0 folder3”代表该片段无限循环显示,且两次显示的间隔为0s,与下一个片段间隔0s,该片段的显示图路径为bootanimation.zip/folder3。
  • “c 1 10 folder4”代表该片段显示1次,显示后暂停10*(1/20)=0.5s,该片段的显示图路径为bootanimation.zip/folder4。

  上面的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息。这些信息都保存在Animation对象animation中,其中,每一个动画片断都使用一个Animation::Part对象来描述,并且保存在Animation对象animation的成员变量parts所描述的一个片断列表中。

Part.3:

  接下来,BootAnimation类的成员函数movie再断续将每一个片断所对应的png图片读取出来。每一个png图片都表示一个动画帧,使用一个Animation::Frame对象来描述,并且保存在对应的Animation::Part对象的成员变量frames所描述的一个帧列表中。

Part.4:

  前面的一系列gl函数首先用来清理屏幕,接下来的一系列gl函数用来设置OpenGL的纹理显示方式。

  变量xc和yc的值用来描述开机动画的显示位置,即需要在屏幕中间显示开机动画,另外一个变量frameDuration的值用来描述每一帧的显示时间,它是以纳秒为单位的。

  Region对象clearReg用来描述屏幕中除了开机动画之外的其它区域,它是用整个屏幕区域减去开机动画所点据的区域来得到的。

Part.5:

<

pre>
bool BootAnimation::movie(){

}

  1. 第一层for循环用来显示每一个动画片断,第二层的for循环用来循环显示每一个动画片断,第三层的for循环用来显示每一个动画片断所对应的png图片。这些png图片以纹理的方式来显示在屏幕中。

  2. 注意,如果一个动画片断的循环显示次数不等于1,那么就说明这个动画片断中的png图片需要重复地显示在屏幕中。由于每一个png图片都需要转换为一个纹理对象之后才能显示在屏幕中,因此,为了避免重复地为同一个png图片创建纹理对象,第三层的for循环在第一次显示一个png图片的时候,会调用函数glGenTextures来为这个png图片创建一个纹理对象,并且将这个纹理对象的名称保存在对应的Animation::Frame对象的成员变量tid中,这样,下次再显示相同的图片时,就可以使用前面已经创建好了的纹理对象,即调用函数glBindTexture来指定当前要操作的纹理对象。

  3. 如果Region对象clearReg所包含的区域不为空,那么在调用函数glDrawTexiOES和eglSwapBuffers来显示每一个png图片之前,首先要将它所包含的区域裁剪掉,避免开机动画可以显示在指定的位置以及大小中。

  4. 每当显示完成一个png图片之后,都要将变量frameDuration的值从纳秒转换为毫秒。如果转换后的值大小于,那么就需要调用函数usleep函数来让线程睡眠一下,以保证每一个png图片,即每一帧动画都按照预先指定好的速度来显示。注意,函数usleep指定的睡眠时间只能精确到毫秒,因此,如果预先指定的帧显示时间小于1毫秒,那么BootAnimation类的成员函数movie是无法精确地控制地每一帧的显示时间的。

  5. 还有另外一个地方需要注意的是,每当循环显示完成一个片断时,需要调用usleep函数来使得线程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照预先设定的节奏来显示开机动画。

  6. 最后一个if语句判断一个动画片断是否是循环显示的,即循环次数不等于1。如果是的话,那么就说明前面为它所对应的每一个png图片都创建过一个纹理对象。现在既然这个片断的显示过程已经结束了,因此,就需要释放前面为它所创建的纹理对象。

附注:

  可以看到,如果exitPending()返回值为true且part.playUntilComplete=false,则会break。即:当SurfaceFlinger服务要求bootanimation停止显示动画时,以‘p’标识的片段会停止,而以’c’标识的片段会继续显示。这就是两者之间的主要区别。 我猜想”c”标识的意思是continue,即:即使SurfaceFlinger要求bootanimation停止动画,bootanimation也不会立刻停止动画,它会等c标识片段都显示完毕后,再停止。

  至此,第三个开机画面的显示过程就分析完成了。

开机动画的停止

  接下来,我们再继续分析第三个开机画面是如何停止显示的。

  当System进程将系统中的关键服务启动起来之后,就会将Launcher启动起来。Android应用程序的启动过程实际上就是它的根Activity组件的启动过程。对于应用程序Launcher来说,它的根Activity组件即为Launcher组件。

  一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。

  如果一个线程想要在空闲的时候处理一些事务,那么就必须要向这个线程的消息队列注册一个空闲消息处理器。自定义的空闲消息处理器灯必须要从MessageQueue.IdleHandler类继承下来,并且重写成员函数queueIdle。当一个线程空闲的时候,即消息队列中没有新的消息需要处理的时候,那些注册了的空闲消息处理器的成员函数queueIdle就会被调用。

  应用程序的主线程是通过ActivityThread类来描述的,它实现在文件frameworks/base/core/Java/android/app/ActivityThread.java中。每当有一个新的Activity组件启动起来的时候,ActivityThread类都会向它所描述的应用程序主线程的消息队列注册一个类型为Idler的空闲消息处理器。这样一个应用程序的主线程就可以在空闲的时候,向ActivityManagerService发送一个Activity组件空闲通知,相当于是通知ActivityManagerService,一个新的Activity组件已经准备就绪了。

  Idler类定义在frameworks/base/core/java/android/app/ActivityThread.java中, 它的成员函数queueIdle的实现如下所示:

  ActivityThread类有一个类型为ActivityClientRecord的成员变量mNewActivities,用来描述所有在当前应用程序主线程中新启动起来的Activity组件。这些新启动起来的Activity组件通过ActivityClientRecord类的成员变量nextIdle连接在一起。一旦当前应用程序主线程向ActivityManagerService发送了这些新启动的Activity组件的空闲通知之后,这些新启动起来的Activity组件就不会再被保存在ActivityThread类的成员变量mNewActivities中了,即每一个新启动的Activity组件只有一次机会向ActivityManagerService发送一个空闲通知。

  向ActivityManagerService发送一个Activity组件空闲通知是通过调用ActivityManagerService代理对象的成员函数activityIdle来实现的,而ActivityManagerService代理对象可以通过调用ActivityManagerNative类的静态成员函数getDefault来获得。

  ActivityManagerService代理对象的类型为ActivityManagerProxy,它的成员函数activityIdle实现在文件frameworks/base/core/java/android/app/ActivityManagerNative.java中,如下所示:

  ActivityManagerProxy类的成员函数activityIdle实际上是向ActivityManagerService发送一个类型为ACTIVITY_IDLE_TRANSACTION的Binder进程间通信请求,其中,参数token用来描述与这个进程间通信请求所关联的一个Activity组件,在我们这个场景中,这个Activity组件即为应用程序Launcher的根Activity组件Launcher。

  类型为ACTIVITY_IDLE_TRANSACTION的Binder进程间通信请求是由ActivityManagerService类的成员函数activityIdle来处理的,如下所示:

  ActivityManagerService有一个类型为ActivityStackSupervisor的成员变量mStackSupervisor,Run all ActivityStacks through this,运行所有的ActivityStacks 通过这个,它的成员函数activityIdleInternalLocked如下:

  所以进入if判断中,将booting置为true,mService.mBooted和enableScreen也为true,进入下面的if判断,ActivityManagerService调用postFinishBooting方法完成系统启动。我们进入往下看:

  这里最后调用enableScreenAfterBoot方法,以便可以将屏幕让出来显示应用程序Launcher的界面:

  ActivityManagerService类的成员变量mWindowManager指向了系统中的Window管理服务WindowManagerService,ActivityManagerService服务通过调用它的成员函数enableScreenAfterBoot来停止显示开机动画。

  WindowManagerService类的成员函数enableScreenAfterBoot的实现如下所示:

  WindowManagerService类的成员变量mSystemBooted用来记录系统是否已经启动完成的。如果已经启动完成的话,那么这个成员变量的值就会等于true,这时候WindowManagerService类的成员函数enableScreenAfterBoot什么也不做就返回了,否则的话,WindowManagerService类的成员函数enableScreenAfterBoot首先将这个成员变量的值设置为true,接着再调用另外一个成员函数performEnableScreen来执行停止显示开机动画的操作。

  WindowManagerService类的成员函数performEnableScreen的实现如下所示:

  WindowManagerService类的另外一个成员变量mDisplayEnabled用来描述WindowManagerService是否已经初始化过系统的屏幕了,只有当它的值等于false,并且系统已经完成启动,即WindowManagerService类的成员变量mSystemBooted等于true的情况下,WindowManagerService类的成员函数performEnableScreen才通知SurfaceFlinger服务停止显示开机动画。

  注意,WindowManagerService类的成员函数performEnableScreen是通过一个类型为IBinder.FIRST_CALL_TRANSACTION的进程间通信请求来通知SurfaceFlinger服务停止显示开机动画的。

  在SurfaceFlinger服务,类型为IBinder.FIRST_CALL_TRANSACTION的进程间通信请求被定义为停止显示开机动画的请求,位于frameworks/native/include/gui/ISurfaceComposer.h中:

  BnSurfaceComposer类它是SurfaceFlinger服务所要继承的Binder本地对象类,其中。当SurfaceFlinger服务接收到类型为IBinder::FIRST_CALL_TRANSACTION,即类型为BOOT_FINISHED的进程间通信请求时,它就会将该请求交给它的成员函数bootFinished来处理。

  SurfaceFlinger服务的成员函数bootFinished实现在文件frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中,如下所示:

  可以看到,该函数将属性“service.bootanim.exit”设置为”1”。在第2节分析android()代码的时候,我们讲到:当属性“service.bootanim.exit”值被设为”1”时,android()就会退出,开机动画显示自然也就结束了。由于android()退出且返回值为false,BootAnimation::threadLoop()线程也就结束了。再回到BootAnimation.cpp的main()函数中,threadLoop()线程结束,main函数也就结束,至此,bootanimaiton进程就自行结束,开机动画的显示完成了。

  至此,Android系统的三个开机画面的显示过程就分析完成了。

小结

本文的目的并不是单纯为了介绍Android系统的开机画面,而是希望能够以Android系统的开机画面来作为切入点来分析SurfaceFlinger。后续文章我们会详细分析SurfaceFlinger的每个模块和功能。

转载自:http://windrunnerlihuan.com/2017/05/02/Android-SurfaceFlinger-%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E4%B8%89-Android%E5%BC%80%E6%9C%BA%E5%8A%A8%E7%94%BB%E6%B5%81%E7%A8%8B%E7%AE%80%E8%BF%B0/

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注