1.简介.


  Android 开机动画,老罗里面流程狠清楚了,这只是记录bootanimation相关。


2.制作bootanimation.zip

2.1bootanimation.zip的文件目录:
bootanimation.zip
---- fd0        (文件夹名可自定义)
----------xx.png  (数目可多)
........          (可放多个文件)
desc.txt              (描述文件)
2.2 desc.txt文件解析
第一行:显示图片的分辨率宽度  高度 帧速(fps)
第二行:p  播放次数(0:表示循环)  在两次循环显示之间的时间间隔(单位:1帧的显示时间)  图片所在文件夹名
....... (如第二行一样,对含有图片的文件夹进行配置)
例子:若bootanimatiom.zip 中 若文件夹fd0中有5张有效图片,fd1中有1张有效图片,desc.txt文件如下配置:
800 480 2
   p 1 5 fd0
   p 1 5 fd1
   p 0 0 fd2
其中 "800 480 2"表示显示图片为 800 * 480 ,帧率为2(即:每张图片显示0.5s),"p 1 5 fd0"表示播放一遍,循环的间隔为 5*1/1(这里只播放一遍,则会停留在最
后一张图片的时间),显示的图片为文件夹fd0中的图片。
2.3压缩文件
注意:压缩的格式为ZIP,方式:存储,在window压缩的时候,需注意压缩后文件时候,压缩包不能含有中间路径(即:压缩的时候,不能包含bootanimatiom这个文件名)
压缩完成后,需要检查。
2.4push 到手机,
push到手机的路径为"/oem/media/bootanimation.zip"或者"/system/media/bootanimation.zip" 源码中定义代码路径:
/frameworks/base/cmds/bootanimation/BootAnimation.cpp

[cpp] view plain copy
#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"


 2.5在android5.0以后还有audio_conf.txt可以配置audio播放相关内容内容,我还没有配置成功过,(待续)


3.代码流程:


   部分内容转自:也借鉴的内容


   

Android studio打开虚拟机提示项目keep stopping_EGL


3.1bootanimation的启动




其中bootanimation的中的配置为disabled,则表明不自启动,其中启动的地方为frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp


void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");  ////=1退出动画 
    property_set("ctl.start", "bootanim");
}

3.2


frameworks/base/cmds/bootanimation/bootanimation_main.cpp


[cpp] view plain 

int main(int argc, char** argv)
{
#if defined(HAVE_PTHREADS)
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
#endif

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    int noBootAnimation = atoi(value);
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {

        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation();

        IPCThreadState::self()->joinThreadPool();

    }
    return 0;
}


 


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

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


ootAnimation类的成员函数onFirstRef实现在文件frameworks/base/cmds/bootanimation/BootAnimation.cpp中,如下所示:



[cpp]  view plain  copy


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

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


[cpp]  view plain  copy



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



             SurfaceComposerClient类内部有一个实现了ISurfaceComposerClient接口的Binder代理对象mClient,这个Binder代理对象引用了SurfaceFlinger服务,SurfaceComposerClient类就是通过它来和SurfaceFlinger服务通信的。

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




    该函数启动了一个BootAnimation线程,用于显示开机动画。由于BootAnimation继承了Thread类,当调用父类的run()时,会在在这个线程运行前,调用readyToRun(),进行一些初始化工作。


    [html]  view plain  copy



    1. status_t BootAnimation::readyToRun() {  
    2.   
    1.   mAssets.addDefaultAssets();  
    2.   
    3. <IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(  
    4.             ISurfaceComposer::eDisplayIdMain));  
    5.     DisplayInfo dinfo;  
    6. status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);  
    7.     if (status)  
    8.         return -1;      
    9. <SurfaceControl> control = session()->createSurface(String8("BootAnimation"),  
    10.            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);     
    11. <Surface> s = control->getSurface();  
    12.   
    13.     // initialize opengl and egl  
    14.     const EGLint attribs[] = {  
    15.             EGL_RED_SIZE,   8,  
    16.             EGL_GREEN_SIZE, 8,  
    17.             EGL_BLUE_SIZE,  8,  
    18.             EGL_DEPTH_SIZE, 0,  
    19.             EGL_NONE  
    20.     };  
    21.     EGLint w, h, dummy;  
    22.     EGLint numConfigs;  
    23.     EGLConfig config;  
    24.     EGLSurface surface;  
    25.     EGLContext context;  
    26.   
    27. display = eglGetDisplay(EGL_DEFAULT_DISPLAY);  
    28.   
    29.     eglInitialize(display, 0, 0);  
    30.     eglChooseConfig(display, attribs, &config, 1, &numConfigs);  
    31. surface = eglCreateWindowSurface(display, config, s.get(), NULL);  
    32. context = eglCreateContext(display, config, NULL, NULL);  
    33.     eglQuerySurface(display, surface, EGL_WIDTH, &w);  
    34.     eglQuerySurface(display, surface, EGL_HEIGHT, &h);  
    35.   
    36.     if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)  
    37.         return NO_INIT;  
    38.   
    39. mDisplay = display;  
    40. mContext = context;  
    41. mSurface = surface;  
    42. mWidth = w;  
    43. mHeight = h;  
    44. mFlingerSurfaceControl = control;  
    45. mFlingerSurface = s;  
    46.   
    47.     // If the device has encryption turned on or is in process  
    48.     // of being encrypted we show the encrypted boot animation.  
    49.     char decrypt[PROPERTY_VALUE_MAX];  
    50.     property_get("vold.decrypt", decrypt, "");  
    51.   
    52. encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);  
    53.   
    54. zipFile = NULL;  
    55.     if ((encryptedAnimation &&  
    56.             (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&  
    57. zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||              
    58.             ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&  
    59. zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||  
    60.             ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&  
    61. zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {  
    62. mZip = zipFile;  
    63.     }  
    64.     return NO_ERROR;  
    65. }

    readyToRun函数主要做了一下几个工作:

    第一,调用SurfaceComposerClient对象mSession的成员函数createSurface,获得一个SurfaceControl对象control,然后调用control的成员函数getSurface,获得一个Surface对象s。control和s都可以与SurgaceFlinger通过binder进行通信。

    第二,初始化IOPENEGL和EGL。主要是四个参数:EGLDisplay对象display,用来描述一个EGL显示屏;EGLConfig对象config,用来描述一个EGL帧缓冲区配置参数;EGLSurface对象surface,用来描述一个EGL绘图表面;EGLContext对象context,用来描述一个EGL绘图上下文。

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



    "


            为了能够在OpenGL和Android窗口系统之间的建立一个桥梁,我们需要一个EGLDisplay对象display,一个EGLConfig对象config,一个EGLSurface对象surface,以及一个EGLContext对象context,其中,EGLDisplay对象display用来描述一个EGL显示屏,EGLConfig对象config用来描述一个EGL帧缓冲区配置参数,EGLSurface对象surface用来描述一个EGL绘图表面,EGLContext对象context用来描述一个EGL绘图上下文(状态),它们是分别通过调用egl库函数eglGetDisplay、EGLUtils::selectConfigForNativeWindow、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之后,就可以通过其内部的Binder代理对象mSurface来请求SurfaceFlinger服务返回帧缓冲区硬件设备的一个图形访问接口。这样,OpenGL最终就可以将要绘制的图形渲染到帧缓冲区硬件设备中去,即显示在实际屏幕上。屏幕的大小,即宽度和高度,可以通过函数eglQuerySurface来获得。
     "


    [cpp]  view plain  copy



    1.  bool BootAnimation::threadLoop()
    2.  {
    
    3.     bool r;
    4.     // We have no bootanimation file, so we use the stock android logo
    5.     // animation.
    6.     if (mZip == NULL) {
    7.         r = android();
    8.     } else {
    9.         r = movie();
    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. }


     如果BootAnimation类的成员变量mAndroidAnimation的值等于true,那么接下来就会调用BootAnimation类的成员函数android来显示系统默认的开机动画,否则的话,就会调用BootAnimation类的成员函数movie来显示用户自定义的开机动画。显示完成之后,就会销毁前面所创建的EGLContext对象mContext、EGLSurface对象mSurface,以及EGLDisplay对象mDisplay等。





    3.3 movie()的实现


    播放bootanimation.zip


         a.读取desc.txt和audio_cof.txt文件


    iew plain   copy


    1.  .............
    2.        if (!readFile("desc.txt", desString)) {
             return false;
         }
         char const* s = desString.string();
    
    
         // Create and initialize an AudioPlayer if we have an audio_conf.txt file
         String8 audioConf;
         if (readFile("audio_conf.txt", audioConf)) {
             mAudioPlayer = new AudioPlayer;
             if (!mAudioPlayer->init(audioConf.string())) {
                 ALOGE("mAudioPlayer.init failed");
                 mAudioPlayer = NULL;
             }
         }3. .............

    desc.txt文件



    [cpp]  view plain  copy



      1.  
       ............
          for (;;) {
              const char* endl = strstr(s, "\n");
              if (!endl) break;
              String8 line(s, endl - s);
              const char* l = line.string();
              int fps, width, height, count, pause;
              char path[ANIM_ENTRY_NAME_MAX];
              char color[7] = "000000"; // default to black if unspecified
      
      
              char pathType;
              if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
                  // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
                  animation.width = width;
                  animation.height = height;
                  animation.fps = fps;
              }
              else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
                  // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
                  Animation::Part part;
                  part.playUntilComplete = pathType == 'c';
                  part.count = count;
                  part.pause = pause;
                  part.path = path;
                  part.audioFile = NULL;
                  if (!parseColor(color, part.backgroundColor)) {
                      ALOGE("> invalid color '#%s'", color);
                      part.backgroundColor[0] = 0.0f;
                      part.backgroundColor[1] = 0.0f;
                      part.backgroundColor[2] = 0.0f;
                  }
                  animation.parts.add(part);
              }
      
      
              s = ++endl;
          }2. .............


             c.读取图片和Audio内容



      [cpp]  view plain  copy


      1.      // read all the data structures
           const size_t pcount = animation.parts.size();
           void *cookie = NULL;
           if (!mZip->startIteration(&cookie)) {
               return false;
           }
      
      
           ZipEntryRO entry;
           char name[ANIM_ENTRY_NAME_MAX];
           while ((entry = mZip->nextEntry(cookie)) != NULL) {
               const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
               if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
                   ALOGE("Error fetching entry file name");
                   continue;
               }
      
      
               const String8 entryName(name);
               const String8 path(entryName.getPathDir());
               const String8 leaf(entryName.getPathLeaf());
               if (leaf.size() > 0) {
                   for (size_t j=0 ; j<pcount ; j++) {
                       if (path == animation.parts[j].path) {
                           int method;
                           // supports only stored png files
                           if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
                               if (method == ZipFileRO::kCompressStored) {
                                   FileMap* map = mZip->createEntryFileMap(entry);
                                   if (map) {
                                       Animation::Part& part(animation.parts.editItemAt(j));
                                       if (leaf == "audio.wav") {
                                           // a part may have at most one audio file
                                           part.audioFile = map;
                                       } else {
                                           Animation::Frame frame;
                                           frame.name = leaf;
                                           frame.map = map;
                                           part.frames.add(frame);
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
           }

          d.之后的代码就是依次进行图片的显示相关的代码和,在checkExit()后进行退出。

      4.开机音乐(很多内容来源于网站,因为开发板的播放音乐还没搞好,之后再测试一下,先记录)






        在5.0之前第一种 方法。(据说这种方法不行了)






      使用MediaPlay实现,思路大致如下:

      Makefile文件中添加:



      [html] view plain copy




        1. LOCAL_SHARED_LIBRARIES += \  
        2.     libmedia





        BootAnimation.cpp中添加:


        [html] view plain copy




          1. #include <system/audio.h>  
          2.   
          3.   
          4. bool BootAnimation :: soundplay()  
          5. {  
          6. mfd = open(xxxxx, O_RDONLY); //xxxxx为音乐文件  
          7.       
          8. mp = new MediaPlayer();  
          9. >setDataSource(mfd, 0, 0x7ffffffffffffffLL);  
          10. >setAudioStreamType(/*AUDIO_STREAM_MUSIC*/AUDIO_STREAM_SYSTEM);  
          11. >prepare();  
          12. >start();  
          13. }  
          14.   
          15. bool BootAnimation::soundstop()  
          16. {  
          17.     if (mp != NULL)   
          18. >stop();  
          19. }


          在movie()中,播放开机动画前调用soundplay,结束时调用soundstop。




          5.0版本后确实源码中提供了AudioPlayer类,用作播放音乐。本部分内容,还没有配置好,之后再更新,待续。