当前位置:   article > 正文

Android——开机动画_安卓九系统开机动画是什么

安卓九系统开机动画是什么

          android系统的开机动画可分为三个部分,kernel启动,init进程启动,android系统服务启动。这三个开机动画都是在一个叫做 帧缓冲区(frame buffer)的硬件设备上进行渲染绘制的。

在Linux内核中,每一个硬件设备都有一个主设备号和一个从设备号,它们用来唯一地标识一个硬件设备。对于帧缓冲区硬件设备来说,它们的主设备号定义为FB_MAJOR(29),而从设备号则与注册的顺序有关,它们的值依次等于0,1,2等。

每一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb<minor>,其中,<minor>表示一个从设备号。例如,第一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb0。用户空间的应用程序通过这个

设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去


一.kernel启动动画:

kernel的启动画面在一般的android系统里面都是没有出现的,在kernel的config里面有这么两项:

  1. # CONFIG_FRAMEBUFFER_CONSOLE is not set
  2. # CONFIG_LOGO is not set

我这里是没有打开的,第一个代表支持帧缓冲控制台,第二个代表显示logo。
在编译控制台的位置:
Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support
Device Drivers ---> Graphics support ---> Bootup logo

kernel中的ainimation 基本上都是在kernel/drivers/video/fbmem.c这个文件中实现。
我的kernel版本是:
  1. VERSION = 3
  2. PATCHLEVEL = 1
  3. SUBLEVEL = 10
  4. EXTRAVERSION =
  5. NAME = "Divemaster Edition"

通过一系列的初始化和准备,最后调用kernel/drivers/video/logo.c中的fb_find_logo(...)来保存kernel 动画的内容:

  1. const struct linux_logo * __init_refok fb_find_logo(int depth)
  2. {
  3. const struct linux_logo *logo = NULL;
  4. if (nologo)
  5. return NULL;
  6. if (depth >= 1) {
  7. #ifdef CONFIG_LOGO_LINUX_MONO
  8. /* Generic Linux logo */
  9. logo = &logo_linux_mono;
  10. #endif
  11. #ifdef CONFIG_LOGO_SUPERH_MONO
  12. /* SuperH Linux logo */
  13. logo = &logo_superh_mono;
  14. #endif
  15. }
  16. if (depth >= 4) {
  17. #ifdef CONFIG_LOGO_LINUX_VGA16
  18. /* Generic Linux logo */
  19. logo = &logo_linux_vga16;
  20. #endif
  21. #ifdef CONFIG_LOGO_BLACKFIN_VGA16
  22. /* Blackfin processor logo */
  23. logo = &logo_blackfin_vga16;
  24. #endif
  25. #ifdef CONFIG_LOGO_SUPERH_VGA16
  26. /* SuperH Linux logo */
  27. logo = &logo_superh_vga16;
  28. #endif
  29. }
  30. if (depth >= 8) {
  31. #ifdef CONFIG_LOGO_LINUX_CLUT224
  32. /* Generic Linux logo */
  33. logo = &logo_linux_clut224;
  34. #endif
  35. #ifdef CONFIG_LOGO_BLACKFIN_CLUT224
  36. /* Blackfin Linux logo */
  37. logo = &logo_blackfin_clut224;
  38. #endif
  39. #ifdef CONFIG_LOGO_DEC_CLUT224
  40. /* DEC Linux logo on MIPS/MIPS64 or ALPHA */
  41. logo = &logo_dec_clut224;
  42. #endif
  43. #ifdef CONFIG_LOGO_MAC_CLUT224
  44. /* Macintosh Linux logo on m68k */
  45. if (MACH_IS_MAC)
  46. logo = &logo_mac_clut224;
  47. #endif
  48. #ifdef CONFIG_LOGO_PARISC_CLUT224
  49. /* PA-RISC Linux logo */
  50. logo = &logo_parisc_clut224;
  51. #endif
  52. #ifdef CONFIG_LOGO_SGI_CLUT224
  53. /* SGI Linux logo on MIPS/MIPS64 and VISWS */
  54. logo = &logo_sgi_clut224;
  55. #endif
  56. #ifdef CONFIG_LOGO_SUN_CLUT224
  57. /* Sun Linux logo */
  58. logo = &logo_sun_clut224;
  59. #endif
  60. #ifdef CONFIG_LOGO_SUPERH_CLUT224
  61. /* SuperH Linux logo */
  62. logo = &logo_superh_clut224;
  63. #endif
  64. #ifdef CONFIG_LOGO_M32R_CLUT224
  65. /* M32R Linux logo */
  66. logo = &logo_m32r_clut224;
  67. #endif
  68. }
  69. return logo;
  70. }

这个函数帧缓冲区硬件设备的颜色深度depth,以及不同的编译选项来获取内容的指针。
在kernel/include/linux/linux_logo.h中定义了这些内容结构体:
  1. extern const struct linux_logo logo_linux_mono;
  2. extern const struct linux_logo logo_linux_vga16;
  3. extern const struct linux_logo logo_linux_clut224;
  4. extern const struct linux_logo logo_blackfin_vga16;
  5. extern const struct linux_logo logo_blackfin_clut224;
  6. extern const struct linux_logo logo_dec_clut224;
  7. extern const struct linux_logo logo_mac_clut224;
  8. extern const struct linux_logo logo_parisc_clut224;
  9. extern const struct linux_logo logo_sgi_clut224;
  10. extern const struct linux_logo logo_sun_clut224;
  11. extern const struct linux_logo logo_superh_mono;
  12. extern const struct linux_logo logo_superh_vga16;
  13. extern const struct linux_logo logo_superh_clut224;
  14. extern const struct linux_logo logo_m32r_clut224;
  15. extern const struct linux_logo logo_spe_clut224;

这些结构体变量保存kernel/drivers/video/logo/下的 ***.ppm 以及.pbm的文件内容。
通过logo指针保存了动画内容之后往后走就要来渲染了,调用到fbmem.c中的:


  1. int fb_show_logo(struct fb_info *info, int rotate)
  2. {
  3. int y;
  4. y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
  5. num_online_cpus());
  6. y = fb_show_extra_logos(info, y, rotate);
  7. return y;
  8. }

同一个文件往下走调用到:
  1. static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
  2. int rotate, unsigned int num)
  3. {
  4. unsigned int x;
  5. if (rotate == FB_ROTATE_UR) {
  6. for (x = 0;
  7. x < num && image->dx + image->width <= info->var.xres;
  8. x++) {
  9. info->fbops->fb_imageblit(info, image);
  10. image->dx += image->width + 8;
  11. }
  12. } else if (rotate == FB_ROTATE_UD) {
  13. for (x = 0; x < num && image->dx >= 0; x++) {
  14. info->fbops->fb_imageblit(info, image);
  15. image->dx -= image->width + 8;
  16. }
  17. } else if (rotate == FB_ROTATE_CW) {
  18. for (x = 0;
  19. x < num && image->dy + image->height <= info->var.yres;
  20. x++) {
  21. info->fbops->fb_imageblit(info, image);
  22. image->dy += image->height + 8;
  23. }
  24. } else if (rotate == FB_ROTATE_CCW) {
  25. for (x = 0; x < num && image->dy >= 0; x++) {
  26. info->fbops->fb_imageblit(info, image);
  27. image->dy -= image->height + 8;
  28. }
  29. }
  30. }
其中:
FB_ROTATE_UR  正常显示
FB_ROTATE_UD  上下倒置
FB_ROTATE_CW  顺时针转90度
FB_ROTATE_CCW  逆时针转90度
到这里就开始调用帧缓冲区硬件设备渲染指定的图像。



二.init进程启动动画:

init进程的启动又要从android下的/system/core/init/init.c的main函数开始了:

  1. int main(int argc, char **argv)
  2. {
  3. int fd_count = 0;
  4. struct pollfd ufds[4];
  5. char *tmpdev;
  6. char* debuggable;
  7. char tmp[32];
  8. int property_set_fd_init = 0;
  9. int signal_fd_init = 0;
  10. int keychord_fd_init = 0;
  11. bool is_charger = false;
  12. if (!strcmp(basename(argv[0]), "ueventd"))
  13. return ueventd_main(argc, argv);
  14. if (!strcmp(basename(argv[0]), "watchdogd"))
  15. return watchdogd_main(argc, argv);
  16. ...
  17. queue_builtin_action(console_init_action, "console_init");
  18. ...
  19. }



这里会根据传进来的参数argv[0]判断进程名,因为首先启动的是init进程,之后会根据init.rc配置ueventd以及watchogd等进程,在这里,这两个进程加载的执行文件也是从这个init文件开始会以这个main函数为入口,所以加了判别。


在这里向init进程的action执行队列添加了一个console_init_action的action,init进程执行相应操作,具体可查看 http://blog.csdn.net/jscese/article/details/18700903,最后依次执行action队列,执行同文件下的:

  1. static int console_init_action(int nargs, char **args)
  2. {
  3. int fd;
  4. char tmp[PROP_VALUE_MAX];
  5. if (console[0]) {
  6. snprintf(tmp, sizeof(tmp), "/dev/%s", console);
  7. console_name = strdup(tmp);
  8. }
  9. fd = open(console_name, O_RDWR);
  10. if (fd >= 0)
  11. have_console = 1;
  12. close(fd);
  13. if( load_565rle_image(INIT_IMAGE_FILE) ) {
  14. fd = open("/dev/tty0", O_WRONLY);
  15. if (fd >= 0) {
  16. const char *msg;
  17. msg = "\n"
  18. "\n"
  19. "\n"
  20. "\n"
  21. "\n"
  22. "\n"
  23. "\n" // console is 40 cols x 30 lines
  24. "\n"
  25. "\n"
  26. "\n"
  27. "\n"
  28. "\n"
  29. "\n"
  30. "\n"
  31. " A N D R O I D ";
  32. write(fd, msg, strlen(msg));
  33. close(fd);
  34. }
  35. }
  36. return 0;
  37. }

初始化控制台,通过dev/console设备来访问控制台,如果有的话 have_console 全局变量被设置为1.
开始加载显示init进程的动画,通过load_565rle_image(INIT_IMAGE_FILE)函数,INIT_IMAGE_FILE 宏定义指定动画内容位置,定义在init.h中
#define INIT_IMAGE_FILE	"/initlogo.rle"

实现在/system/core/init/logo.c中:
  1. int load_565rle_image(char *fn)
  2. {
  3. struct FB fb;
  4. struct stat s;
  5. unsigned short *data, *bits, *ptr;
  6. unsigned count, max;
  7. int fd;
  8. if (vt_set_mode(1))
  9. return -1;
  10. fd = open(fn, O_RDONLY);
  11. if (fd < 0) {
  12. ERROR("cannot open '%s'\n", fn);
  13. goto fail_restore_text;
  14. }
  15. if (fstat(fd, &s) < 0) {
  16. goto fail_close_file;
  17. }
  18. data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
  19. if (data == MAP_FAILED)
  20. goto fail_close_file;
  21. if (fb_open(&fb))
  22. goto fail_unmap_data;
  23. max = fb_width(&fb) * fb_height(&fb);
  24. ptr = data;
  25. count = s.st_size;
  26. bits = fb.bits;
  27. while (count > 3) {
  28. unsigned n = ptr[0];
  29. if (n > max)
  30. break;
  31. android_memset16(bits, ptr[1], n << 1);
  32. bits += n;
  33. max -= n;
  34. ptr += 2;
  35. count -= 4;
  36. }
  37. munmap(data, s.st_size);
  38. fb_update(&fb);
  39. fb_close(&fb);
  40. close(fd);
  41. unlink(fn);
  42. return 0;
  43. fail_unmap_data:
  44. munmap(data, s.st_size);
  45. fail_close_file:
  46. close(fd);
  47. fail_restore_text:
  48. vt_set_mode(0);
  49. return -1;
  50. }

调用:
  1. static int vt_set_mode(int graphics)
  2. {
  3. int fd, r;
  4. fd = open("/dev/tty0", O_RDWR | O_SYNC);
  5. if (fd < 0)
  6. return -1;
  7. r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));
  8. close(fd);
  9. return r;
  10. }


打开控制控制台设备文件/dev/tty0,设置控制台的显示方式,传入参数为1,控制台将以图形方式显示。

往下就是打开传入的动画内容文件,fstat计算大小,通过mmap将 initlogo.rle内容映射到init进程地址空间,

再打开/dev/graphics/fb0,通过打开这个设备文件就可以访问帧缓冲区设备,获取帧缓冲区设备的固定信息和可变信息,然后映射到init进程地址空间,

获取屏幕的宽高,计算出帧缓冲区能写入的大小,就知道在init进程地址空间上的大小,通过一个while循环将initlogo.rle内容写入到帧缓冲区设备上去。

更新init进程开机画面到屏幕上面:
  1. static void fb_update(struct FB *fb)
  2. {
  3. fb->vi.yoffset = 1;
  4. ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
  5. fb->vi.yoffset = 0;
  6. ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
  7. }
通过帧缓冲区设备渲染。


三.系统服务启动动画:



这个动画是android系统最常用的一个动画,由bootainimation来负责显示运行,这在init.rc里面是作为一个service:

1.init.rc 配置:

  1. service bootanim /system/bin/bootanimation
  2. class main
  3. user graphics
  4. group graphics
  5. disabled
  6. oneshot


这个是disabled,init启动解析的时候是不会被启动的。


2.SurfaceFinger服务的启动:

在前文 http://blog.csdn.net/jscese/article/details/17115395#t7 中启动到system server部分启动了系统所需的一些服务时,在 system_init.cpp中启动视频服务时会启动一个SurfaceFlinger的服务,获取一个SurfaceFinger的实例。
SurfaceFlinger类继承于BinderService模板类,BinderService类的instantiate()函数就是构造对应类型的服务对象,并注册到ServiceManager进程中/frameworks/native/include/binder/BinderService.h:

  1. static void instantiate() { publish(); }
  2. static status_t publish(bool allowIsolated = false) {
  3. sp<IServiceManager> sm(defaultServiceManager());
  4. return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
  5. }

调用到/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中构造:

  1. SurfaceFlinger::SurfaceFlinger()
  2. : BnSurfaceComposer(), Thread(false),
  3. mTransactionFlags(0),
  4. mTransactionPending(false),
  5. mAnimTransactionPending(false),
  6. mLayersRemoved(false),
  7. mRepaintEverything(0),
  8. mBootTime(systemTime()),
  9. mVisibleRegionsDirty(false),
  10. mHwWorkListDirty(false),
  11. mDebugRegion(0),
  12. mDebugDDMS(0),
  13. mDebugDisableHWC(0),
  14. mDebugDisableTransformHint(0),
  15. mDebugInSwapBuffers(0),
  16. mLastSwapBufferTime(0),
  17. mDebugInTransaction(0),
  18. mLastTransactionTime(0),
  19. mBootFinished(false)
  20. {
  21. ALOGI("SurfaceFlinger is starting");
  22. // debugging stuff...
  23. char value[PROPERTY_VALUE_MAX];
  24. property_get("debug.sf.showupdates", value, "0");
  25. mDebugRegion = atoi(value);
  26. property_get("debug.sf.ddms", value, "0");
  27. mDebugDDMS = atoi(value);
  28. if (mDebugDDMS) {
  29. if (!startDdmConnection()) {
  30. // start failed, and DDMS debugging not enabled
  31. mDebugDDMS = 0;
  32. }
  33. }
  34. ALOGI_IF(mDebugRegion, "showupdates enabled");
  35. ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
  36. }

这里的构造只是做了一些初始化,获取系统开机属性,设置一些变量。

因为SurfaceFlinger又继承于RefBase类,并重写了该类的onFirstRef()函数,我们知道,RefBase类的子类对象在第一次创建时,会自动调用onFirstRef()函数,因此在SurfaceFlinger对象构造完成时,将调用onFirstRef()函数。
  1. void SurfaceFlinger::onFirstRef()
  2. {
  3. mEventQueue.init(this);
  4. run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
  5. // Wait for the main thread to be done with its initialization
  6. mReadyToRunBarrier.wait();
  7. }

这里就从SurfaceFlinger的父类Thread创建一个新的线程,加入threadLoop函数,调用到 readyToRun()函数:
  1. status_t SurfaceFlinger::readyToRun()
  2. {
  3. ALOGI( "SurfaceFlinger's main thread ready to run. "
  4. "Initializing graphics H/W...");
  5. ...
  6. // initialize OpenGL ES
  7. DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext);
  8. initializeGL(mEGLDisplay);
  9. // start the EventThread
  10. mEventThread = new EventThread(this);
  11. mEventQueue.setEventThread(mEventThread);
  12. ...
  13. // start boot animation
  14. startBootAnim();
  15. return NO_ERROR;
  16. }


3.SurfaceFinger中开启bootainimation服务:

开始执行bootainimation。

  1. void SurfaceFlinger::startBootAnim() {
  2. // start boot animation
  3. property_set("service.bootanim.exit", "0");
  4. property_set("ctl.start", "bootanim");
  5. }

设置系统属性ctl.start 的值为bootanim。android的系统属性启动以及应用可以参考 http://blog.csdn.net/jscese/article/details/18700903
到这里就启动了一个新的服务进程bootanim,/frameworks/base/cmds/bootanimation/bootanimation_main.cpp


  1. int main(int argc, char** argv)
  2. {
  3. #if defined(HAVE_PTHREADS)
  4. setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
  5. #endif
  6. char value[PROPERTY_VALUE_MAX];
  7. property_get("debug.sf.nobootanimation", value, "0");
  8. int noBootAnimation = atoi(value);
  9. ALOGI_IF(noBootAnimation, "boot animation disabled");
  10. if (!noBootAnimation) {
  11. ALOGD("boot animation start");
  12. sp<ProcessState> proc(ProcessState::self());
  13. ProcessState::self()->startThreadPool();
  14. // create the boot animation object
  15. sp<BootAnimation> boot = new BootAnimation();
  16. IPCThreadState::self()->joinThreadPool();
  17. }
  18. return 0;
  19. }

获取nobootanimation这个属性的值,判断如果为0的话
那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象。这个BootAnimation对象就是用来显示第三个开机画面的。由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信,因此,应用程序BootAnimation就需要启动一个Binder线程池。
BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,
因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用/frameworks/base/cmds/bootanimation/BootAnimation.cpp:
  1. void BootAnimation::onFirstRef() {
  2. status_t err = mSession->linkToComposerDeath(this);
  3. ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
  4. if (err == NO_ERROR) {
  5. run("BootAnimation", PRIORITY_DISPLAY);
  6. }
  7. }
同时构造:
  1. BootAnimation::BootAnimation() : Thread(false)
  2. {
  3. mSession = new SurfaceComposerClient();
  4. }

mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信的。

4.bootanimation线程构建:

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


  1. status_t BootAnimation::readyToRun() {
  2. mAssets.addDefaultAssets();
  3. ...
  4. mAndroidAnimation = true;
  5. // If the device has encryption turned on or is in process
  6. // of being encrypted we show the encrypted boot animation.
  7. char decrypt[PROPERTY_VALUE_MAX];
  8. property_get("vold.decrypt", decrypt, "");
  9. bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
  10. if ((encryptedAnimation &&
  11. (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
  12. (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||
  13. ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
  14. (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) ||
  15. ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
  16. (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
  17. mAndroidAnimation = false;
  18. }
  19. return NO_ERROR;
  20. }

在这里为绘制做准备,包括访问到帧缓冲区设备,获取屏幕显示大小等。
判断是否为android默认开机动画。

  1. #define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
  2. #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
  3. #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"

上面的文件只要有一个存在就代表不是android默认的 而是用户自定义的开机动画。

5.bootanimation执行动画:

准备工作做完了 就执行:

  1. bool BootAnimation::threadLoop()
  2. {
  3. bool r;
  4. if (mAndroidAnimation) {
  5. r = android();
  6. } else {
  7. r = movie();
  8. }
  9. eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  10. eglDestroyContext(mDisplay, mContext);
  11. eglDestroySurface(mDisplay, mSurface);
  12. mFlingerSurface.clear();
  13. mFlingerSurfaceControl.clear();
  14. eglTerminate(mDisplay);
  15. IPCThreadState::self()->stopProcess();
  16. return r;
  17. }

1.android默认动画:
如果是android默认的开机动画:

  1. bool BootAnimation::android()
  2. {
  3. initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
  4. initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
  5. // clear screen
  6. glShadeModel(GL_FLAT);
  7. glDisable(GL_DITHER);
  8. glDisable(GL_SCISSOR_TEST);
  9. ...
  10. }

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


2.用户自定义动画:
如果为用户自定义的开机动画:

  1. bool BootAnimation::movie()
  2. {
  3. ZipFileRO& zip(mZip);
  4. size_t numEntries = zip.getNumEntries();
  5. ZipEntryRO desc = zip.findEntryByName("desc.txt");
  6. FileMap* descMap = zip.createEntryFileMap(desc);
  7. ALOGE_IF(!descMap, "descMap is null");
  8. if (!descMap) {
  9. return false;
  10. }
  11. String8 desString((char const*)descMap->getDataPtr(),
  12. descMap->getDataLength());
  13. char const* s = desString.string();
  14. Animation animation;
  15. // Parse the description file
  16. for (;;) {
  17. const char* endl = strstr(s, "\n");
  18. if (!endl) break;
  19. String8 line(s, endl - s);
  20. const char* l = line.string();
  21. int fps, width, height, count, pause;
  22. char path[256];
  23. char pathType;
  24. if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
  25. // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
  26. // ALOGD("jscese> w=%d, h=%d", mWidth, mHeight);
  27. animation.width =width;
  28. animation.height =height;
  29. animation.fps = fps;
  30. }
  31. else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) {
  32. //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
  33. Animation::Part part;
  34. part.playUntilComplete = pathType == 'c';
  35. part.count = count;
  36. part.pause = pause;
  37. part.path = path;
  38. animation.parts.add(part);
  39. }
  40. s = ++endl;
  41. }
  42. ...
  43. }

找到解析bootanimation.zip 压缩包,找到desc.txt 文件,这个文件描述了显示的相关的特性,包括分辨率以及显示的帧率:

  1. 1920 1080 24
  2. p 1 0 part1
  3. p 0 10 part2

第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。剩余的每一行都用来描述一个动画片断,这些行必须要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。第一个数字表示一个片断的循环显示次数,如果它的值等于0,那么就表示无限循环地显示该动画片断。第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时间间隔是以一个帧的时间为单位的。文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。


以上面这个desct.txt文件的内容为例,它描述了一个大小为1920 x 1080的开机动画,动画的显示速度为24帧每秒。这个开机动画包含有两个片断part1和part2。片断part1只显示一次,它对应的png图片保存在目录part1中。片断part2无限循环地显示,其中,每两次循环显示的时间间隔为10 x (1 / 24)秒,它对应的png图片保存在目录part2中。


往后就是收集信息,通过帧缓冲区设备有次序的渲染这些图片了。

特别注意,android开机动画的zip包是不能压缩的! 在ubuntu下可以使用一下命令制作bootanimation.zip:

zip -r -0 bootanimation.zip bootanimation/ desc.txt



6.bootanimation停止:

1.activity的空闲通知:
当android开机启动到了Launcher,第一个被启动起来的应用程序的主线程空闲的时候,这个activity就会向ActivityManagerService发送一个Activity组件空闲的通知,
每当有一个新的Activity组件启动起来的时候,ActivityThread类都会向它所描述的应用程序主线程的消息队列注册一个类型为Idler的空闲消息处理器。这样一个应用程序的主线程就可以在空闲的时候,向ActivityManagerService发送一个Activity组件空闲通知,相当于是通知ActivityManagerService,一个新的Activity组件已经准备就绪了。
通过一系列的调用到/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中的:
  1. public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
  2. final long origId = Binder.clearCallingIdentity();
  3. ActivityRecord r = mMainStack.activityIdleInternal(token, false, config);
  4. ...
  5. }

调用到/frameworks/base/services/java/com/android/server/am/ActivityStack.java中的:
  1. final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout,
  2. Configuration config) {
  3. if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
  4. ActivityRecord res = null;
  5. ...
  6. if (mMainStack) {
  7. if (!mService.mBooted) {
  8. mService.mBooted = true;
  9. enableScreen = true;
  10. }
  11. }
  12. ...
  13. if (enableScreen)
  14. {
  15. mService.enableScreenAfterBoot();
  16. }
  17. ...
  18. }


回到ActivityManagerService.java中:
  1. void enableScreenAfterBoot() {
  2. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
  3. SystemClock.uptimeMillis());
  4. mWindowManager.enableScreenAfterBoot();
  5. synchronized (this) {
  6. updateEventDispatchingLocked();
  7. }
  8. }

2.WindowManagerService通信SurfaceFinger设置property:

调用到了 WindowManagerService.java中的:
  1. public void performEnableScreen()
  2. {
  3. synchronized(mWindowMap) {
  4. if (DEBUG_BOOT) {
  5. RuntimeException here = new RuntimeException("here");
  6. here.fillInStackTrace();
  7. ...
  8. try {
  9. IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
  10. if (surfaceFlinger != null) {
  11. //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
  12. Parcel data = Parcel.obtain();
  13. data.writeInterfaceToken("android.ui.ISurfaceComposer");
  14. surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
  15. data, null, 0);
  16. data.recycle();
  17. }
  18. ...
  19. }


通过一个类型为IBinder.FIRST_CALL_TRANSACTION的进程间通信请求来通知SurfaceFlinger服务停止显示开机动画的.
  1. void SurfaceFlinger::bootFinished()
  2. {
  3. const nsecs_t now = systemTime();
  4. ...
  5. property_set("service.bootanim.exit", "1");
  6. }

最后还是通过设置android的系统属性通知到init守护进程,init进程来处理property的改变,在这里就是停掉bootanimation这个服务进程,bootanimation动画就停止掉了。





声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/625297
推荐阅读
相关标签
  

闽ICP备14008679号