Android系统在启动SystemServer进程时,通过两个阶段来启动系统所有服务,在第一阶段启动本地服务,如SurfaceFlinger,SensorService等,在第二阶段则启动一系列的Java服务。开机动画是在什么时候启动的呢?通过查看源码,Android开机动画是在启动SurfaceFlinger服务时启动的。SystemServer的main函数首先调用init1来启动本地服务,init1函数通过JNI调用C语言中的system_init()函数来实现服务启动。

  1. extern "C" status_t system_init()
  2. {
  3. sp<ProcessState> proc(ProcessState::self());
  4. sp<IServiceManager> sm = defaultServiceManager();
  5. sp<GrimReaper> grim = new GrimReaper();
  6. sm->asBinder()->linkToDeath(grim, grim.get(), 0);
  7. char propBuf[PROPERTY_VALUE_MAX];
  8. property_get("system_init.startsurfaceflinger", propBuf, "1");
  9. if (strcmp(propBuf, "1") == 0) {
  10. // Start the SurfaceFlinger
  11. SurfaceFlinger::instantiate();
  12. }
  13. ...
  14. return NO_ERROR;
  15. }

通过调用SurfaceFlinger::instantiate()函数来启动SurfaceFlinger服务,SurfaceFlinger类继承于BinderService模板类,BinderService类的instantiate()函数就是构造对应类型的服务对象,并注册到ServiceManager进程中。

  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. }

对于SurfaceFlinger服务来说,就是首先构造SurfaceFlinger对象,然后通过调用ServiceManger的远程Binder代理对象的addService函数来注册SurfaceFlinger服务。这里只介绍SurfaceFlinger的构造过程,对于服务注册过程,在Android服务注册完整过程源码分析中已经介绍的非常详细。

  1. SurfaceFlinger::SurfaceFlinger()
  2. : BnSurfaceComposer(), Thread(false),
  3. mTransactionFlags(0),
  4. mTransationPending(false),
  5. mLayersRemoved(false),
  6. mBootTime(systemTime()),
  7. mVisibleRegionsDirty(false),
  8. mHwWorkListDirty(false),
  9. mElectronBeamAnimationMode(0),
  10. mDebugRegion(0),
  11. mDebugDDMS(0),
  12. mDebugDisableHWC(0),
  13. mDebugDisableTransformHint(0),
  14. mDebugInSwapBuffers(0),
  15. mLastSwapBufferTime(0),
  16. mDebugInTransaction(0),
  17. mLastTransactionTime(0),
  18. mBootFinished(false),
  19. mSecureFrameBuffer(0)
  20. {
  21. init();
  22. }

SurfaceFlinger对象实例的构造过程很简单,就是初始化一些成员变量值,然后调用init()函数来完成初始化工作

  1. void SurfaceFlinger::init()
  2. {
  3. char value[PROPERTY_VALUE_MAX];
  4. property_get("debug.sf.showupdates", value, "0");
  5. mDebugRegion = atoi(value);
  6. #ifdef DDMS_DEBUGGING
  7. property_get("debug.sf.ddms", value, "0");
  8. mDebugDDMS = atoi(value);
  9. if (mDebugDDMS) {
  10. DdmConnection::start(getServiceName());
  11. }
  12. #endif
  13. property_get("ro.bootmode", value, "mode");
  14. if (!(strcmp(value, "engtest")
  15. && strcmp(value, "special")
  16. && strcmp(value, "wdgreboot")
  17. && strcmp(value, "unknowreboot")
  18. && strcmp(value, "panic"))) {
  19. SurfaceFlinger::sBootanimEnable = false;
  20. }
  21. }

在SurfaceFlinger的init函数中,也并没有做任何复杂工作,只是简单读取系统属性得到开机模式,来相应设置一些变量而已,比如是否显示开机动画变量sBootanimEnable。由于SurfaceFlinger继承于RefBase类,并重写了该类的onFirstRef()函数,我们知道,RefBase类的子类对象在第一次创建时,会自动调用onFirstRef()函数,因此在SurfaceFlinger对象构造完成时,将调用onFirstRef()函数。

  1. void SurfaceFlinger::onFirstRef()
  2. {
  3. mEventQueue.init(this);//事件队列初始化
  4. run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);//运行SurfaceFlinger线程
  5. mReadyToRunBarrier.wait();
  6. }

这里不对SurfaceFlinger的相关内容做详细介绍,本文的主要内容是介绍开机动画显示过程。由于SurfaceFlinger同时继承于线程Thread类,而且SurfaceFlinger并没有重写Thread类的run方法,因此这里调用SurfaceFlinger的run函数,其实调用的就是其父类Thread的run函数。

  1. status_t Thread::run(const char* name, int32_t priority, size_t stack)
  2. {
  3. Mutex::Autolock _l(mLock);
  4. if (mRunning) {
  5. return INVALID_OPERATION;
  6. }
  7. mStatus = NO_ERROR;
  8. mExitPending = false;
  9. mThread = thread_id_t(-1);
  10. mHoldSelf = this;
  11. mRunning = true;
  12. bool res;
  13. if (mCanCallJava) {
  14. res = createThreadEtc(_threadLoop,this, name, priority, stack, &mThread);
  15. } else {
  16. res = androidCreateRawThreadEtc(_threadLoop,this, name, priority, stack, &mThread);
  17. }
  18. if (res == false) {
  19. mStatus = UNKNOWN_ERROR; // something happened!
  20. mRunning = false;
  21. mThread = thread_id_t(-1);
  22. mHoldSelf.clear(); // "this" may have gone away after this.
  23. return UNKNOWN_ERROR;
  24. }
  25. return NO_ERROR;
  26. }

该函数就是创建一个线程,并运行现在执行函数_threadLoop

  1. int Thread::_threadLoop(void* user)
  2. {
  3. Thread* const self = static_cast<Thread*>(user);
  4. sp<Thread> strong(self->mHoldSelf);
  5. wp<Thread> weak(strong);
  6. self->mHoldSelf.clear();
  7. #ifdef HAVE_ANDROID_OS
  8. self->mTid = gettid();
  9. #endif
  10. bool first = true;
  11. do {
  12. bool result;
  13. if (first) {
  14. first = false;
  15. self->mStatus = self->readyToRun();
  16. result = (self->mStatus == NO_ERROR);
  17. if (result && !self->exitPending()) {
  18. result = self->threadLoop();
  19. }
  20. } else {
  21. result = self->threadLoop();
  22. }
  23. {
  24. Mutex::Autolock _l(self->mLock);
  25. if (result == false || self->mExitPending) {
  26. self->mExitPending = true;
  27. self->mRunning = false;
  28. self->mThread = thread_id_t(-1);
  29. self->mThreadExitedCondition.broadcast();
  30. break;
  31. }
  32. }
  33. strong.clear();
  34. strong = weak.promote();
  35. } while(strong != 0);
  36. return 0;
  37. }

在线程开始运行时,变量first为true,因此会调用self->readyToRun()来做一些初始化工作,同时将变量first设置为false,在以后线程执行过程中,就反复执行self->threadLoop()了。作为Thread类的子类SurfaceFlinger重写了这两个方法,因此创建的SurfaceFlinger线程在执行前会调用SurfaceFlinger的readyToRun()函数完成初始化任务,然后反复执行SurfaceFlinger的threadLoop()函数。

  1. status_t SurfaceFlinger::readyToRun()
  2. {
  3. ALOGI( "SurfaceFlinger's main thread ready to run. "
  4. "Initializing graphics H/W...");
  5. int dpy = 0;
  6. {
  7. // initialize the main display
  8. GraphicPlane& plane(graphicPlane(dpy));
  9. DisplayHardware* const hw = new DisplayHardware(this, dpy);
  10. plane.setDisplayHardware(hw);
  11. }
  12. // create the shared control-block
  13. mServerHeap = new MemoryHeapBase(4096,MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
  14. ALOGE_IF(mServerHeap==0, "can't create shared memory dealer");
  15. mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
  16. ALOGE_IF(mServerCblk==0, "can't get to shared control block's address");
  17. new(mServerCblk) surface_flinger_cblk_t;
  18. // initialize primary screen
  19. const GraphicPlane& plane(graphicPlane(dpy));
  20. const DisplayHardware& hw = plane.displayHardware();
  21. const uint32_t w = hw.getWidth();
  22. const uint32_t h = hw.getHeight();
  23. const uint32_t f = hw.getFormat();
  24. hw.makeCurrent();
  25. // initialize the shared control block
  26. mServerCblk->connected |= 1<<dpy;
  27. display_cblk_t* dcblk = mServerCblk->displays + dpy;
  28. memset(dcblk, 0, sizeof(display_cblk_t));
  29. dcblk->w = plane.getWidth();
  30. dcblk->h = plane.getHeight();
  31. dcblk->format = f;
  32. dcblk->orientation = ISurfaceComposer::eOrientationDefault;
  33. dcblk->xdpi = hw.getDpiX();
  34. dcblk->ydpi = hw.getDpiY();
  35. dcblk->fps = hw.getRefreshRate();
  36. dcblk->density = hw.getDensity();
  37. // Initialize OpenGL|ES
  38. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  39. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  40. glEnableClientState(GL_VERTEX_ARRAY);
  41. glShadeModel(GL_FLAT);
  42. glDisable(GL_DITHER);
  43. glDisable(GL_CULL_FACE);
  44. const uint16_t g0 = pack565(0x0F,0x1F,0x0F);
  45. const uint16_t g1 = pack565(0x17,0x2f,0x17);
  46. const uint16_t wormholeTexData[4] = { g0, g1, g1, g0 };
  47. glGenTextures(1, &mWormholeTexName);
  48. glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
  49. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  50. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  51. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  52. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  53. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,GL_RGB, GL_UNSIGNED_SHORT_5_6_5, wormholeTexData);
  54. const uint16_t protTexData[] = { pack565(0x03, 0x03, 0x03) };
  55. glGenTextures(1, &mProtectedTexName);
  56. glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
  57. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  58. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  59. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  60. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  61. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0,GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
  62. glViewport(0, 0, w, h);
  63. glMatrixMode(GL_PROJECTION);
  64. glLoadIdentity();
  65. // put the origin in the left-bottom corner
  66. glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
  67. // start the EventThread
  68. mEventThread = new EventThread(this);
  69. mEventQueue.setEventThread(mEventThread);
  70. hw.startSleepManagement();
  71. /*
  72. * We're now ready to accept clients...
  73. */
  74. mReadyToRunBarrier.open();
  75. // start boot animation
  76. startBootAnim();
  77. return NO_ERROR;
  78. }

该函数首先是初始化Android的图形显示系统,启动SurfaceFlinger事件线程,这些内容只有了解了Android的显示原理及SurfaceFlinger服务之后才能理解,这里不做介绍。当显示系统初始化完毕后,调用startBootAnim()函数来显示开机动画。

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

startBootAnim()函数比较简单,就是通过判断开机动画的变量值了决定是否显示开机动画。启动开机动画进程也是通过Android属性系统来实现的,具体启动过程可以查看 Android 系统属性SystemProperty分析。在Android系统启动脚本init.rc中配置了开机动画服务进程。

property_set("ctl.start", "bootanim")就是启动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. /*modify boot animation and added shutdown animation*/
  12. char argvtmp[2][BOOTANIMATION_PATHSET_MAX];
  13. memset(argvtmp[0],0,BOOTANIMATION_PATHSET_MAX);
  14. memset(argvtmp[1],0,BOOTANIMATION_PATHSET_MAX);
  15. //没有参数时,执行开机动画,
  16. if(argc<2){
  17. //开机动画文件BOOTANIMATION_BOOT_FILM_PATH_DEFAULT="/system/media/bootanimation.zip"
  18. strncpy(argvtmp[0],BOOTANIMATION_BOOT_FILM_PATH_DEFAULT,BOOTANIMATION_PATHSET_MAX);
  19. //开机声音文件BOOTANIMATION_BOOT_SOUND_PATH_DEFAULT="/system/media/bootsound.mp3"
  20. strncpy(argvtmp[1],BOOTANIMATION_BOOT_SOUND_PATH_DEFAULT,BOOTANIMATION_PATHSET_MAX);
  21. }else{//否则执行关机动画
  22. //关机动画文件BOOTANIMATION_SHUTDOWN_FILM_PATH_DEFAULT="/system/media/shutdownanimation.zip"
  23. strncpy(argvtmp[0],BOOTANIMATION_SHUTDOWN_FILM_PATH_DEFAULT,BOOTANIMATION_PATHSET_MAX);
  24. //关机声音文件BOOTANIMATION_SHUTDOWN_SOUND_PATH_DEFAULT="/system/media/shutdownsound.mp3"
  25. strncpy(argvtmp[1],BOOTANIMATION_SHUTDOWN_SOUND_PATH_DEFAULT,BOOTANIMATION_PATHSET_MAX);
  26. }
  27. __android_log_print(ANDROID_LOG_INFO,"BootAnimation", "begine bootanimation!");
  28. //启动Binder线程池,用于接收其他进程的请求
  29. sp<ProcessState> proc(ProcessState::self());
  30. ProcessState::self()->startThreadPool();
  31. //创建BootAnimation对象
  32. BootAnimation *boota = new BootAnimation();
  33. String8 descname("desc.txt");
  34. if(argc<2){//设置开机动画文件的默认路径
  35. String8 mpath_default(BOOTANIMATION_BOOT_FILM_PATH_DEFAULT);
  36. String8 spath_default(BOOTANIMATION_BOOT_SOUND_PATH_DEFAULT);
  37. boota->setmoviepath_default(mpath_default);
  38. boota->setsoundpath_default(spath_default);
  39. //boota->setdescname_default(descname_default);
  40. }else {//设置关机动画文件的默认路径
  41. String8 mpath_default(BOOTANIMATION_SHUTDOWN_FILM_PATH_DEFAULT);
  42. String8 spath_default(BOOTANIMATION_SHUTDOWN_SOUND_PATH_DEFAULT);
  43. boota->setmoviepath_default(mpath_default);
  44. boota->setsoundpath_default(spath_default);
  45. //boota->setdescname_default(descname_default);
  46. __android_log_print(ANDROID_LOG_INFO,"BootAnimation","shutdown exe bootanimation!");
  47. }
  48. String8 mpath(argvtmp[0]);
  49. String8 spath(argvtmp[1]);
  50. //设置动画的文件路径
  51. boota->setmoviepath(mpath);
  52. boota->setsoundpath(spath);
  53. boota->setdescname(descname);
  54. __android_log_print(ANDROID_LOG_INFO,"BootAnimation","%s", mpath.string());
  55. __android_log_print(ANDROID_LOG_INFO,"BootAnimation","%s", spath.string());
  56. sp<BootAnimation> bootsp = boota;
  57. //将当前线程注册到Binder线程池中
  58. IPCThreadState::self()->joinThreadPool();
  59. }
  60. return 0;
  61. }

该函数构造了一个BootAnimation对象,并且为该对象设置了开关机动画及声音文件路径,同时创建了Binder线程池,并将bootanim进程的主线程注册到Binder线程池中,用于接收客户进程的Binder通信请求。

  1. BootAnimation::BootAnimation() : Thread(false)
  2. {
  3. mSession = new SurfaceComposerClient();
  4. }

在构造BootAnimation对象时,实例化SurfaceComposerClient对象,用于请求SurfaceFlinger显示开关机动画。由于BootAnimation类继承于RefBase,同时重写了onFirstRef()函数,因此在构造BootAnimation对象时,会调用该函数。

  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. }

该函数首先为SurfaceComposerClient对象注册Binder死亡通知,然后调用BootAnimation的run方法,由于BootAnimation同时继承于Thread类,前面介绍SurfaceFlinger时已经介绍到,当某个类继承于Thread类时,当调用该类的run函数时,函数首先会执行readyToRun()函数来完成线程执行前的一些工作,然后线程反复执行threadLoop()函数,在BootAnimation类中,同样重新了这两个方法

  1. status_t BootAnimation::readyToRun() {
  2. //force screen display in vertical layout
  3. mSession->setOrientation(0, 0, 0);
  4. mAssets.addDefaultAssets();
  5. DisplayInfo dinfo;
  6. status_t status = session()->getDisplayInfo(0, &dinfo);
  7. if (status)
  8. return -1;
  9. // create the native surface
  10. sp<SurfaceControl> control;
  11. if (dinfo.w > dinfo.h) {
  12. control = session()->createSurface(0, dinfo.h, dinfo.w, PIXEL_FORMAT_RGB_565);
  13. } else {
  14. control = session()->createSurface(0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
  15. }
  16. SurfaceComposerClient::openGlobalTransaction();
  17. control->setLayer(0x40000000);
  18. SurfaceComposerClient::closeGlobalTransaction();
  19. sp<Surface> s = control->getSurface();
  20. // initialize opengl and egl
  21. const EGLint attribs[] = {
  22. EGL_RED_SIZE, 8,
  23. EGL_GREEN_SIZE, 8,
  24. EGL_BLUE_SIZE, 8,
  25. EGL_DEPTH_SIZE, 0,
  26. EGL_NONE
  27. };
  28. EGLint w, h, dummy;
  29. EGLint numConfigs;
  30. EGLConfig config;
  31. EGLSurface surface;
  32. EGLContext context;
  33. EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  34. eglInitialize(display, 0, 0);
  35. eglChooseConfig(display, attribs, &config, 1, &numConfigs);
  36. surface = eglCreateWindowSurface(display, config, s.get(), NULL);
  37. context = eglCreateContext(display, config, NULL, NULL);
  38. eglQuerySurface(display, surface, EGL_WIDTH, &w);
  39. eglQuerySurface(display, surface, EGL_HEIGHT, &h);
  40. if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
  41. return NO_INIT;
  42. mDisplay = display;
  43. mContext = context;
  44. mSurface = surface;
  45. mWidth = w;
  46. mHeight = h;
  47. mFlingerSurfaceControl = control;
  48. mFlingerSurface = s;
  49. mAndroidAnimation = true;
  50. // If the device has encryption turned on or is in process
  51. // of being encrypted we show the encrypted boot animation.
  52. char decrypt[PROPERTY_VALUE_MAX];
  53. property_get("vold.decrypt", decrypt, "");
  54. bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
  55. //如果"/system/media/bootanimation-encrypted.zip"文件存在或者设置的动画文件存在,或者默认动画文件存在,或者"/data/local/bootanimation.zip"文件存在,都显示开机动画文件,否则显示Android滚动字样
  56. if ((encryptedAnimation &&
  57. (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
  58. (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||
  59. ((access(moviepath, R_OK) == 0) &&
  60. (mZip.open(moviepath) == NO_ERROR)) ||
  61. ((access(movie_default_path, R_OK) == 0) &&
  62. (mZip.open(movie_default_path) == NO_ERROR)) ||
  63. ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
  64. (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR))) {
  65. mAndroidAnimation = false;
  66. }
  67. return NO_ERROR;
  68. }

在该函数里创建SurfaceControl对象,通过SurfaceControl对象得到Surface对象,并初始化好OpenGL,同时判断动画文件是否存在,如果不存在,则设置标志位mAndroidAnimation为true,表示显示Android滚动字样。当初始化完这些必需资源后,线程进入循环执行体threadLoop()

  1. bool BootAnimation::threadLoop()
  2. {
  3. bool r;
  4. //如果mAndroidAnimation为true,表示动画文件不存在,则显示Android滚动字样
  5. if (mAndroidAnimation) {
  6. r = android();
  7. } else {//显示动画
  8. r = movie();
  9. }
  10. //资源回收
  11. eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  12. eglDestroyContext(mDisplay, mContext);
  13. eglDestroySurface(mDisplay, mSurface);
  14. mFlingerSurface.clear();
  15. mFlingerSurfaceControl.clear();
  16. eglTerminate(mDisplay);
  17. IPCThreadState::self()->stopProcess();
  18. return r;
  19. }

开机画面主要是由一个zip格式的压缩包bootanimation.zip组成,压缩包里面包含数张png格式的图片,还有一个desc.txt的文本文档,开机时按desc.txt里面的指令,屏幕上会按文件名称顺序连续的播放一张张的图片,就像播放原始的胶带影片一样,形成动画。desc.txt是一个保存形式为ANSI格式的文件,用于设置这个动画像素(大小),帧数,闪烁次数,文件夹名称等。内容如下:
480 854 10

p 1 2 folder1

p 0 2 folder2

480 427 30  ---这里的480代表图片的像素(大小)宽度,427代表图片的像素(大小)高度,30代表帧数;

p 1 0 part0 ---这里的p代表标志符,1代表循环次数为1次,0代表阶段间隔时间为0,part0代表对应的文件夹名,为第一阶段动画图片目录;

p 0 0 part1---这里的p代表标志符,0代表本阶段无限循环,0代表阶段间隔时间为0,part1代表对应的文件夹名,为第二阶段动画图片目录;

阶段切换间隔时间:单位是一个帧的持续时间,比如帧数是30,那么帧的持续时间就是1秒/30 = 33.3毫秒。阶段切换间隔时间期间开机动画进程进入休眠,把CPU时间让给初始化系统使用。也就是间隔长启动会快,但会影响动画效果。
folder1和folder2文件夹内包含的是两个动画的系列图片,图片为PNG格式。

  1. bool BootAnimation::movie()
  2. {
  3. ZipFileRO& zip(mZip);
  4. //获取zip压缩文件中的文件数目
  5. size_t numEntries = zip.getNumEntries();
  6. //打开zip压缩文件中的desc.txt文件
  7. ZipEntryRO desc = zip.findEntryByName("desc.txt");
  8. FileMap* descMap = zip.createEntryFileMap(desc);
  9. ALOGE_IF(!descMap, "descMap is null");
  10. if (!descMap) {
  11. return false;
  12. }
  13. //读取desc.txt文件内容
  14. String8 desString((char const*)descMap->getDataPtr(),descMap->getDataLength());
  15. char const* s = desString.string();
  16. Animation animation;
  17. //读取persist.sys.silence属性来决定是否播放开机音乐
  18. char silence[PROPERTY_VALUE_MAX];
  19. property_get("persist.sys.silence", silence, "0");
  20. if(strcmp("1", silence)==0){
  21. // do something.
  22. }else{
  23. soundplay();
  24. }
  25. //解析desc.txt文件内容
  26. for (;;) { //从字符串s中查找是否有字符串"\n",如果有,返回s中"\n"起始位置的指针,如果没有,返回null。
  27. const char* endl = strstr(s, "\n");
  28. if (!endl) break;
  29. //取得文件一行内容
  30. String8 line(s, endl - s);
  31. const char* l = line.string();
  32. int fps, width, height, count, pause;
  33. char path[256];
  34. char pathType;
  35. //从文件第一行中读取宽度,高度,帧数
  36. //480 854 10 <---> width height fps
  37. if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
  38. //LOGD("> w=%d, h=%d, fps=%d", fps, width, height);
  39. animation.width = (width > 0 ? width : mWidth);
  40. animation.height = (height > 0 ? height : mHeight);
  41. animation.fps = fps;
  42. //p 1 2 folder1 <---> pathType count pause path
  43. }else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) {
  44. //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
  45. Animation::Part part;//一个part描述一个动画文件夹内容
  46. part.playUntilComplete = pathType == 'c';
  47. part.count = count;
  48. part.pause = pause;
  49. part.path = path;
  50. animation.parts.add(part);
  51. }
  52. s = ++endl;
  53. }
  54. //读取动画个数
  55. const size_t pcount = animation.parts.size();
  56. //遍历zip压缩包中的所有文件
  57. for (size_t i=0 ; i<numEntries ; i++) {
  58. char name[256];
  59. ZipEntryRO entry = zip.findEntryByIndex(i);
  60. //读取压缩包中的文件名称,所在目录的路径
  61. if (zip.getEntryFileName(entry, name, 256) == 0) {
  62. const String8 entryName(name);
  63. const String8 path(entryName.getPathDir());
  64. const String8 leaf(entryName.getPathLeaf());
  65. if (leaf.size() > 0) {
  66. for (int j=0 ; j<pcount ; j++) {
  67. if (path == animation.parts[j].path) {
  68. int method;
  69. //获取文件信息
  70. if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {
  71. if (method == ZipFileRO::kCompressStored) {
  72. FileMap* map = zip.createEntryFileMap(entry);
  73. if (map) {
  74. Animation::Frame frame;
  75. frame.name = leaf;
  76. frame.map = map;
  77. Animation::Part& part(animation.parts.editItemAt(j));
  78. part.frames.add(frame);
  79. }
  80. }
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. // clear screen
  88. glShadeModel(GL_FLAT);
  89. glDisable(GL_DITHER);
  90. glDisable(GL_SCISSOR_TEST);
  91. glDisable(GL_BLEND);
  92. glClearColor(0,0,0,1);
  93. glClear(GL_COLOR_BUFFER_BIT);
  94. eglSwapBuffers(mDisplay, mSurface);
  95. glBindTexture(GL_TEXTURE_2D, 0);
  96. glEnable(GL_TEXTURE_2D);
  97. glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  98. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  99. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  100. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  101. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  102. const int xc = (mWidth - animation.width) / 2;
  103. const int yc = ((mHeight - animation.height) / 2);
  104. nsecs_t lastFrame = systemTime();
  105. nsecs_t frameDuration = s2ns(1) / animation.fps;
  106. Region clearReg(Rect(mWidth, mHeight));
  107. clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
  108. for (int i=0 ; i<pcount ; i++) {
  109. const Animation::Part& part(animation.parts[i]);
  110. const size_t fcount = part.frames.size();
  111. glBindTexture(GL_TEXTURE_2D, 0);
  112. for (int r=0 ; !part.count || r<part.count ; r++) {
  113. // Exit any non playuntil complete parts immediately
  114. if(exitPending() && !part.playUntilComplete)
  115. break;
  116. for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
  117. const Animation::Frame& frame(part.frames[j]);
  118. nsecs_t lastFrame = systemTime();
  119. if (r > 0) {
  120. glBindTexture(GL_TEXTURE_2D, frame.tid);
  121. } else {
  122. if (part.count != 1) {
  123. glGenTextures(1, &frame.tid);
  124. glBindTexture(GL_TEXTURE_2D, frame.tid);
  125. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  126. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  127. }
  128. initTexture(
  129. frame.map->getDataPtr(),
  130. frame.map->getDataLength());
  131. }
  132. if (!clearReg.isEmpty()) {
  133. Region::const_iterator head(clearReg.begin());
  134. Region::const_iterator tail(clearReg.end());
  135. glEnable(GL_SCISSOR_TEST);
  136. while (head != tail) {
  137. const Rect& r(*head++);
  138. glScissor(r.left, mHeight - r.bottom,
  139. r.width(), r.height());
  140. glClear(GL_COLOR_BUFFER_BIT);
  141. }
  142. glDisable(GL_SCISSOR_TEST);
  143. }
  144. glDrawTexiOES(xc, yc, 0, animation.width, animation.height);
  145. eglSwapBuffers(mDisplay, mSurface);
  146. nsecs_t now = systemTime();
  147. nsecs_t delay = frameDuration - (now - lastFrame);
  148. //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
  149. lastFrame = now;
  150. if (delay > 0) {
  151. struct timespec spec;
  152. spec.tv_sec = (now + delay) / 1000000000;
  153. spec.tv_nsec = (now + delay) % 1000000000;
  154. int err;
  155. do {
  156. err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
  157. } while (err<0 && errno == EINTR);
  158. }
  159.  
  160. checkExit();
  161. }
  162. usleep(part.pause * ns2us(frameDuration));
  163.  
  164. // For infinite parts, we've now played them at least once, so perhaps exit
  165. if(exitPending() && !part.count)
  166. break;
  167. }
  168. // free the textures for this part
  169. if (part.count != 1) {
  170. for (int j=0 ; j<fcount ; j++) {
  171. const Animation::Frame& frame(part.frames[j]);
  172. glDeleteTextures(1, &frame.tid);
  173. }
  174. }
  175. }
  176. soundstop();
  177. return false;
  178. }

Android 开机动画源码分析的更多相关文章

  1. OpenGL—Android 开机动画源码分析一

    .1 Android开机动画实现方式目前实现Android开机动画的方式主要是逐帧动画和OpenGL动画. ?逐帧动画 逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在“连续的 ...

  2. OpenGL—Android 开机动画源码分析二

    引自http://blog.csdn.net/luoshengyang/article/details/7691321/ BootAnimation类的成员函数的实现比较长,我们分段来阅读: 第三个开 ...

  3. Android 属性动画 源码解析 深入了解其内部实现

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42056859,本文出自:[张鸿洋的博客] 我参加了博客之星评选,如果你喜欢我的博 ...

  4. Android网络框架源码分析一---Volley

    转载自 http://www.jianshu.com/p/9e17727f31a1?utm_campaign=maleskine&utm_content=note&utm_medium ...

  5. Android分包MultiDex源码分析

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/52845661 本文出自: [HansChen的博客] 概述 Android开发者应该 ...

  6. Android消息机制源码分析

    本篇主要介绍Android中的消息机制,即Looper.Handler是如何协同工作的: Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper Handler:用来将消息(Me ...

  7. Android开源框架源码分析:Okhttp

    一 请求与响应流程 1.1 请求的封装 1.2 请求的发送 1.3 请求的调度 二 拦截器 2.1 RetryAndFollowUpInterceptor 2.2 BridgeInterceptor ...

  8. android hardware.c 源码分析

    android的jni通过ID来找hal模块,使用了hw_get_module()函数,本文就通过这个函数的来分析一下hal层的模块是如何匹配的. 首先要了解三个结构体hw_module_t,hw_m ...

  9. Android -- 消息处理机制源码分析(Looper,Handler,Message)

    android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...

随机推荐

  1. java内存映射文件

    内存映射文件能够让我们创建和修改大文件(大到内存无法读入得文件),对于内存映射文件,我们可以认为是文件已经全部被读入到内存当中,然后当成一个大的数字来访问,简化修改文件的代码. 1.directBuf ...

  2. Product(大数相乘)

    Description The problem is to multiply two integers X, Y. (0<=X,Y<10250) Input The input will ...

  3. 关于playframework2.5

    加入了很多新东西: 1.用akka streams 替换了大部分 iteratee-based async io,当然还有一些模块在用iteratees 2.java 的一些API 做了调整升级,以及 ...

  4. Struts2中获取HttpServletRequest,HttpSession等的几种方式

    转自:http://www.kaifajie.cn/struts/8944.html package com.log; import java.io.IOException; import java. ...

  5. angularjs学习总结(快速预览版)

    对html标签的增强 -> 指令 指令的本质是什么 声明的方式调用相应的脚本,实现一些操作,声明的所在的dom就是脚本的执行上下文? 自定义标签 -- 标签指令自定义属性 -- 属性指令特定格式 ...

  6. Delphi中三种方法获取Windows任务栏的高度

    第一种:需要引用Windows单元 ShowMessage(IntToStr(GetSystemMetrics(SM_CYSCREEN)-GetSystemMetrics(SM_CYFULLSCREE ...

  7. 有关FTPS和VNP的详解

    http://hfang.blog.51cto.com/4449017/811744 http://www.h3c.com.cn/MiniSite/Technology_Circle/Technolo ...

  8. hdoj 1247 Hat’s Words(字典树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1247 思路分析:题目要求找出在输入字符串中的满足要求(该字符串由输入的字符串中的两个字符串拼接而成)的 ...

  9. C++类成员常量

    由于#define 定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用const 修饰数据成员来实现.const 数据成员的确是存在的,但其含义却不是我们所期望的.const 数据成员只在某个 ...

  10. Zookeeper 在Hadoop中的应用

    Zookeeper 简单介绍 Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目.它主要是用来解决分布式应用中常常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集 ...