Android 开机动画bootanimation

来源:互联网 发布:5g网络产业链 编辑:程序博客网 时间:2024/05/22 09:49

1.简介.

  Android 开机动画,老罗http://blog.csdn.net/luoshengyang/article/details/7691321/里面流程狠清楚了,这只是记录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

  1. #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.代码流程:

   部分内容转自:http://blog.csdn.net/happy_6678/article/details/46236831也借鉴http://blog.csdn.net/luoshengyang/article/details/7691321/的内容
   技术分享

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;}
copy

这个函数首先检查系统属性“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.     }
       mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信的,它是在BootAnimation类的构造函数中创建的,如下所示:

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

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()的实现
     android()函数主要播放一般的Android字样的函数,这里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. .............

     b.解析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.开机音乐(很多内容来源于网站,因为开发板的播放音乐还没搞好,之后再测试一下,先记录)

     http://blog.csdn.net/wangkaiblog/article/details/70255969
 在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.     mp->setDataSource(mfd, 0, 0x7ffffffffffffffLL);  
  10.     mp->setAudioStreamType(/*AUDIO_STREAM_MUSIC*/AUDIO_STREAM_SYSTEM);  
  11.     mp->prepare();  
  12.     mp->start();  
  13. }  
  14.   
  15. bool BootAnimation::soundstop()  
  16. {  
  17.     if (mp != NULL)   
  18.         mp->stop();  
  19. }  
在movie()中,播放开机动画前调用soundplay,结束时调用soundstop。


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


原创粉丝点击