<Android Framework 之路>BootAnimation(2)

来源:互联网 发布:如何删掉淘宝评价图片 编辑:程序博客网 时间:2024/05/16 15:13

前言

上一篇主要讲解了BootAnimation是从何而来,如何启动,从开机,到SurfaceFlinger服务起来,然后到执行开机动画,如果要深入的看里面的代码,是需要花一定的时间的,我们旨在了解大致的流程,具体流程中的函数,变量意义,具体实现,读者请自研。由来已知,执行待述~

###BootAnimation执行
1. 代码位置
frameworks/base/cmds/bootanimation
目录中包含如下文件
这里写图片描述

文件名 作用 Android.mk mk文件,编译模块使用 AudioPlayer.cpp、AudioPlayer.h 音频播放 BootAnimation.cpp、BootAnimation.h 开机动画的源文件和头文件 bootanimation_main.cpp 开机动画的入口



2. 源码分析
bootanimation_main.cpp
文件中定义main函数,也就是C语言中的执行文件的入口函数

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) {        //创建ProcessSate对象        // 这个过程会打开/dev/binder设备,形成和内核binder机制的交互的通道; 映射fd到内存        sp<ProcessState> proc(ProcessState::self());        //创建线程并加入到线程池        ProcessState::self()->startThreadPool();        // 创建开机动画对象        sp<BootAnimation> boot = new BootAnimation();        //把主线程加入到线程池        IPCThreadState::self()->joinThreadPool();    }    return 0;}

创建开机动画对象会执行到BootAnimation的构造方法,先看下BootAnimation的头文件
BootAnimation.h

......namespace android {class AudioPlayer;class Surface;class SurfaceComposerClient;class SurfaceControl;// ---------------------------------------------------------------------------class BootAnimation : public Thread, public IBinder::DeathRecipient{public:                BootAnimation();    virtual     ~BootAnimation();    sp<SurfaceComposerClient> session() const;private:   virtual bool        threadLoop();   virtual status_t    readyToRun();   virtual void        onFirstRef();   virtual void        binderDied(const wp<IBinder>& who);   //Texture类定义   struct Texture {       GLint   w; //宽度       GLint   h; //高度       GLuint  name; //名称   };   //动画内容结构体   struct Animation {       //动画帧       struct Frame {           String8 name;           FileMap* map;           mutable GLuint tid;           bool operator < (const Frame& rhs) const {               return name < rhs.name;           }       };       //动画部分,因为动画可能是由几个部分组成       struct Part {           int count;           int pause;           String8 path;           SortedVector<Frame> frames;           bool playUntilComplete;           float backgroundColor[3];           FileMap* audioFile;       };       int fps;       int width;       int height;       Vector<Part> parts;   };   status_t initTexture(Texture* texture, AssetManager& asset, const char* name);   status_t initTexture(const Animation::Frame& frame);   bool android();   bool readFile(const char* name, String8& outString);   bool movie();   void checkExit();   sp<SurfaceComposerClient>       mSession;   sp<AudioPlayer>                 mAudioPlayer;   AssetManager mAssets;   Texture     mAndroid[2];   int         mWidth;   int         mHeight;   EGLDisplay  mDisplay;   EGLDisplay  mContext;   EGLDisplay  mSurface;   sp<SurfaceControl> mFlingerSurfaceControl;   sp<Surface> mFlingerSurface;   ZipFileRO   *mZip;};// ---------------------------------------------------------------------------}; // namespace android#endif // ANDROID_BOOTANIMATION_H

大致的定义和声明就是这么多,看下具体实现
BootAnimation.cpp
首先执行构造方法

BootAnimation::BootAnimation() : Thread(false), mZip(NULL){    //用于界面显示的mSession,与SurfaceFlinger交互的客户端    mSession = new SurfaceComposerClient();}

然后执行

void BootAnimation::onFirstRef() {    status_t err = mSession->linkToComposerDeath(this);    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));    if (err == NO_ERROR) {        run("BootAnimation", PRIORITY_DISPLAY);    }}

由于BootAnimation继承Thread类,首先会调用readyToRun函数

status_t BootAnimation::readyToRun() {    mAssets.addDefaultAssets();    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(            ISurfaceComposer::eDisplayIdMain));    DisplayInfo dinfo;    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);    if (status)        return -1;    // create the native surface    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);    SurfaceComposerClient::openGlobalTransaction();    control->setLayer(0x40000000);    SurfaceComposerClient::closeGlobalTransaction();    sp<Surface> s = control->getSurface();    // initialize opengl and egl    const EGLint attribs[] = {            EGL_RED_SIZE,   8,            EGL_GREEN_SIZE, 8,            EGL_BLUE_SIZE,  8,            EGL_DEPTH_SIZE, 0,            EGL_NONE    };    EGLint w, h, dummy;    EGLint numConfigs;    EGLConfig config;    EGLSurface surface;    EGLContext context;    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);    eglInitialize(display, 0, 0);    eglChooseConfig(display, attribs, &config, 1, &numConfigs);    surface = eglCreateWindowSurface(display, config, s.get(), NULL);    context = eglCreateContext(display, config, NULL, NULL);    eglQuerySurface(display, surface, EGL_WIDTH, &w);    eglQuerySurface(display, surface, EGL_HEIGHT, &h);    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)        return NO_INIT;    mDisplay = display;    mContext = context;    mSurface = surface;    mWidth = w;    mHeight = h;    mFlingerSurfaceControl = control;    mFlingerSurface = s;    // If the device has encryption turned on or is in process    // of being encrypted we show the encrypted boot animation.    char decrypt[PROPERTY_VALUE_MAX];    property_get("vold.decrypt", decrypt, "");    bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);    ZipFileRO* zipFile = NULL;    if ((encryptedAnimation &&            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {        mZip = zipFile;    }    return NO_ERROR;}

上面主要做两个操作:
1. 初始化显示界面用于播放开机动画,egl的一些内容;
2. 根据手机是否加密选择不同的开机动画文件,然后拿到入口zipFile
其中用到了一个文件路径在BootAnimation.cpp的开头有定义

#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"   //这个应该是OEM厂商自己定制#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"   //正常情况下的Android原始开机动画#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip" //加密手机的开机动画

readyToRun方法执行完之后,接着看threaLoop函数

bool BootAnimation::threadLoop(){    bool r;    // We have no bootanimation file, so we use the stock android logo    // animation.    if (mZip == NULL) {        r = android(); //没有开机动画文件,执行android logo    } else {        r = movie(); //存在开机动画文件,则执行对应的开机动画文件解析出来的内容    }    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);    eglDestroyContext(mDisplay, mContext);    eglDestroySurface(mDisplay, mSurface);    mFlingerSurface.clear();    mFlingerSurfaceControl.clear();    eglTerminate(mDisplay);    IPCThreadState::self()->stopProcess();    return r;}

——————————————>android()

bool BootAnimation::android(){    //初始化两个纹理用于显示logo    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");    // clear screen 清屏    glShadeModel(GL_FLAT);    glDisable(GL_DITHER);    glDisable(GL_SCISSOR_TEST);    glClearColor(0,0,0,1);    glClear(GL_COLOR_BUFFER_BIT);    eglSwapBuffers(mDisplay, mSurface);    glEnable(GL_TEXTURE_2D);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    const GLint xc = (mWidth  - mAndroid[0].w) / 2;    const GLint yc = (mHeight - mAndroid[0].h) / 2;    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),            updateRect.height());    // Blend state    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    const nsecs_t startTime = systemTime();    //不停的显示知道exitPending()返回true    do {        ......        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);        ......        checkExit();    } while (!exitPending());    glDeleteTextures(1, &mAndroid[0].name);    glDeleteTextures(1, &mAndroid[1].name);    return false;}

主要工作,初始化显示的logo纹理,不断刷新界面直到exitPending()返回true,exitPenging()是Thread类中定义的函数,在checkExit()函数中通过requestExit()来执行退出

void BootAnimation::checkExit() {    // Allow surface flinger to gracefully request shutdown    char value[PROPERTY_VALUE_MAX];    //EXIT_PROP_NAME "service.bootanim.exit" 这个配置项在SurfaceFlinger的bootFinished函数中设置为1,然后这里才能退出开机动画,这个过程设计到开机启动到launcher的整个过程,这里不赘述    property_get(EXIT_PROP_NAME, value, "0");    int exitnow = atoi(value);    if (exitnow) {        requestExit();        if (mAudioPlayer != NULL) {            mAudioPlayer->requestExit();        }    }}

——————————————>movie()
movie()这个函数有点长,我们截断一点一点的看
1. 读取bootanimation.zip中的配置文件

    if (!readFile("desc.txt", desString)) {        return false;    }    char const* s = desString.string();    //读取desc.txt文件    // Create and initialize an AudioPlayer if we have an audio_conf.txt file    String8 audioConf;    //判断是否需要创建AudioPlayer,这部分我们暂时不关注    if (readFile("audio_conf.txt", audioConf)) {        mAudioPlayer = new AudioPlayer;        if (!mAudioPlayer->init(audioConf.string())) {            ALOGE("mAudioPlayer.init failed");            mAudioPlayer = NULL;        }    }
  1. 解析desc.txt文件,就是上面拿到的那个char const *s
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;        }        //或者读取part内容        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;    }
  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);                                }                            }                        }                    }                }            }        }    }    mZip->endIteration(cookie);
  1. 显示动画
    // clear screen    glShadeModel(GL_FLAT);    glDisable(GL_DITHER);    glDisable(GL_SCISSOR_TEST);    glDisable(GL_BLEND);    glClearColor(0,0,0,1);    glClear(GL_COLOR_BUFFER_BIT);    eglSwapBuffers(mDisplay, mSurface);    glBindTexture(GL_TEXTURE_2D, 0);    glEnable(GL_TEXTURE_2D);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    const int xc = (mWidth - animation.width) / 2;    const int yc = ((mHeight - animation.height) / 2);    nsecs_t lastFrame = systemTime();    nsecs_t frameDuration = s2ns(1) / animation.fps;    Region clearReg(Rect(mWidth, mHeight));    clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));    for (size_t i=0 ; i<pcount ; i++) {        const Animation::Part& part(animation.parts[i]);        const size_t fcount = part.frames.size();        glBindTexture(GL_TEXTURE_2D, 0);        for (int r=0 ; !part.count || r<part.count ; r++) {            // Exit any non playuntil complete parts immediately            if(exitPending() && !part.playUntilComplete)                break;            // only play audio file the first time we animate the part            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {                mAudioPlayer->playFile(part.audioFile);            }            glClearColor(                    part.backgroundColor[0],                    part.backgroundColor[1],                    part.backgroundColor[2],                    1.0f);            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {                const Animation::Frame& frame(part.frames[j]);                nsecs_t lastFrame = systemTime();                if (r > 0) {                    glBindTexture(GL_TEXTURE_2D, frame.tid);                } else {                    if (part.count != 1) {                        glGenTextures(1, &frame.tid);                        glBindTexture(GL_TEXTURE_2D, frame.tid);                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);                    }                    initTexture(frame);                }                if (!clearReg.isEmpty()) {                    Region::const_iterator head(clearReg.begin());                    Region::const_iterator tail(clearReg.end());                    glEnable(GL_SCISSOR_TEST);                    while (head != tail) {                        const Rect& r(*head++);                        glScissor(r.left, mHeight - r.bottom,                                r.width(), r.height());                        glClear(GL_COLOR_BUFFER_BIT);                    }                    glDisable(GL_SCISSOR_TEST);                }                glDrawTexiOES(xc, yc, 0, animation.width, animation.height);                eglSwapBuffers(mDisplay, mSurface);                nsecs_t now = systemTime();                nsecs_t delay = frameDuration - (now - lastFrame);                //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));                lastFrame = now;                if (delay > 0) {                    struct timespec spec;                    spec.tv_sec  = (now + delay) / 1000000000;                    spec.tv_nsec = (now + delay) % 1000000000;                    int err;                    do {                        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);                    } while (err<0 && errno == EINTR);                }                checkExit();            }            usleep(part.pause * ns2us(frameDuration));            // For infinite parts, we've now played them at least once, so perhaps exit            if(exitPending() && !part.count)                break;        }        // free the textures for this part        if (part.count != 1) {            for (size_t j=0 ; j<fcount ; j++) {                const Animation::Frame& frame(part.frames[j]);                glDeleteTextures(1, &frame.tid);            }        }    }

备注

主要工作,贴代码,加自己的理解,有问题留言。

1 0
原创粉丝点击