转自:https://blog.csdn.net/u010753159/article/details/51325500 感谢作者。
网上有很多关于android开机动画显示的分析,但大部分是针对于android的早期版本。在android5.1中,开机动画显示的工作流程做了一些修改,下面就针对android5.1,分析一下开机动画的启动、显示和停止的整个过程。
1. bootanimation应用的启动过程
Android系统开机动画的显示是由bootanimation应用实现的。
bootanimation在init.rc中的定义如下:
- service bootanim /system/bin/bootanimation
- class core
- user graphics
- group graphics audio
- disabled
- oneshot
可见,由于设置为"disable",该应用在init启动过程中是不会启动的,需要其他地方显示的调用才能启动。那是什么时候启动的呢?当SurfaceFlinger服务启动时,会修改系统属性值ctl.start,通知init进程启动bootanimation。
在早期的Android版本中,SurfaceFlinger服务是由SystemServer启动的。但在Android5.1中,该服务是init进程启动过程中就启动了。在init.rc中能看到对该服务的描述:
- service surfaceflinger /system/bin/surfaceflinger
- class core
- user system
- group graphics drmrpc
- onrestart restart zygote
SurfaceFlinger服务源码路径为:frameworks\native\services\surfaceflinger
服务的入口在main_surfaceflinger.cpp中,具体为:
- int main(int, char**) {
- // When SF is launched in its own process, limit the number of
- // binder threads to 4.
- ProcessState::self()->setThreadPoolMaxThreadCount(4);
- // start the thread pool
- sp<ProcessState> ps(ProcessState::self());
- ps->startThreadPool();
- // instantiate surfaceflinger
- sp<SurfaceFlinger> flinger = new SurfaceFlinger();
- #if defined(HAVE_PTHREADS)
- setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
- #endif
- set_sched_policy(0, SP_FOREGROUND);
- // initialize before clients can connect
- flinger->init();
- // publish surface flinger
- sp<IServiceManager> sm(defaultServiceManager());
- sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
- // run in this thread
- flinger->run();
- return 0;
- }
下面主要看一下SurfaceFlinger::init()方法,具体代码为:
- void SurfaceFlinger::init() {
- ALOGI( "SurfaceFlinger's main thread ready to run. "
- "Initializing graphics H/W...");
- ......
- // start boot animation
- startBootAnim();
- }
- void SurfaceFlinger::startBootAnim() {
- // start boot animation
- property_set("service.bootanim.exit", "0");
- property_set("ctl.start", "bootanim");
- }
可见,将系统属性ctl.start的值设置为"bootanim"。
回到init进程的init.c的main函数中:
- int main(int argc, char **argv) {
- .......
- for(;;) {
- .......
- nr = poll(ufds, fd_count, timeout);
- if (nr <= 0)
- continue;
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents & POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
- }
- }
- if(memcmp(msg.name,"ctl.",4) == 0) {
- // Keep the old close-socket-early behavior when handling
- // ctl.* properties.
- close(s);
- if (check_control_mac_perms(msg.value, source_ctx)) {
- handle_control_message((char*) msg.name + 4, (char*) msg.value);
- } else {
- ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
- msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
- }
- }
该函数会进一步执行handle_control_message(),传入的参数msg.name=ctl.start,msg.value=bootanim。
- void handle_control_message(const char *msg, const char *arg)
- {
- if (!strcmp(msg,"start")) {
- msg_start(arg);
- } else if (!strcmp(msg,"stop")) {
- msg_stop(arg);
- } else if (!strcmp(msg,"restart")) {
- msg_restart(arg);
- } else {
- ERROR("unknown control msg '%s'\n", msg);
- }
- }
- static void msg_start(const char *name)
- {
- struct service *svc = NULL;
- char *tmp = NULL;
- char *args = NULL;
- if (!strchr(name, ':'))
- svc = service_find_by_name(name);
- else {
- tmp = strdup(name);
- if (tmp) {
- args = strchr(tmp, ':');
- *args = '\0';
- args++;
- svc = service_find_by_name(tmp);
- }
- }
- if (svc) {
- service_start(svc, args);
- } else {
- ERROR("no such service '%s'\n", name);
- }
- if (tmp)
- free(tmp);
- }
到此,bootanimation应用就启动了。
2. 开机动画的显示过程
下面,开始分析bootanimation是如何绘制并在屏幕上显示开机动画的。
代码路径为:frameworks\base\cmds\bootanimation。包括以下几个文件:
Android.mk ----------- 编译文件
BootAnimation.cpp ----------- BootAnimation类的定义和实现
BootAnimation.h ----------- BootAnimation的声明
BootAnimation_main.cpp ----------- 程序入口
AudioPlayer.cpp ----------- 视频播放类的定义和实现
AudioPlayer.h ----------- 视频播放类的声明
先看一下bootanimation的入口,BootAnimation_main.cpp中的main函数:
- 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();
- sp<BootAnimation> boota = new BootAnimation();
- IPCThreadState::self()->joinThreadPool();
- }
- return 0;
- }
Main函数首先获取属性"debug.sf.nobootanimation"的值并判断,如果为1,函数退出,开机动画就不会显示了。如果为0,会开启一个binder线程池,用来在开机动画的过程中,与SurfaceFlinger通信,接着创建一个BootAnimation对象,该对象就是用来显示开机动画的。下面看一下BootAnimation类的声明:
- class BootAnimation : public Thread, public IBinder::DeathRecipient
- {
- public:
- BootAnimation();
- virtual ~BootAnimation();
- .......
- private:
- virtual bool threadLoop();
- virtual status_t readyToRun();
- virtual void onFirstRef();
- virtual void binderDied(const wp<IBinder>& who);
- 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();
- ......
- };
onFirstRef() ----- 属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当有sp包装的类初始化的时候调用;
binderDied() ----- 当对象死掉或者其他情况导致该Binder结束时,就会回调binderDied()方法;
readyToRun() ----- Thread执行前的初始化工作;
threadLoop() ----- 每个线程类都要实现的,在这里定义thread的执行内容。这个函数如果返回true,且没有requestExist()没有被调用,则该函数会再次执行;如果返回false,则threadloop中的内容仅仅执行一次,线程就会退出。
其他主要函数的说明如下:
android() ----- 显示系统默认的开机画面;
movie() ----- 显示用户自定义的开机动画。
BootAnimationL类的构造函数:
- BootAnimation::BootAnimation() : Thread(false), mZip(NULL),mfd(-1)
- {
- mSession = new <span style="font-family: Arial, Helvetica, sans-serif;">SurfaceComposerClient</span>();
- }
主要是new一个SurfaceComposerClient对象,用来和SurfaceFlinger进行binder进程间通信。
由于在BootAnimation_main.cpp的main函数创建BootAnimation对象时,使用了智能指针引用,因此还会调用onFirstRef()函数:
- 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);
- }
- }
- 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;
- sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
- dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
- 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;
- }
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绘图上下文。
第三,读取动画文件。动画文件的读取是按顺序进行的,如果读取成功,则不再读取后续的文件,如果失败,则读取下一个文件。顺序如下:
1--如果设备的加密功能已经开启,或者设备正在进行加密,则读取加密开机动画文件,路径为
- #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
2--OEM厂商指定的开机动画,路径为:
- #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
- #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
线程的初始化工作完成后,就要进入线程的主体函数,完成开机动画的绘制和显示。具体函数为threadLoop():
- bool BootAnimation::threadLoop()
- {
- bool r;
- // We have no bootanimation file, so we use the stock android logo
- // animation.
- if (mZip == NULL) {
- r = android();
- } else {
- r = movie();
- }
- eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglDestroyContext(mDispl</span><span style="font-size:14px;">ay, mSurface);
- mFlingerSurface.clear();
- mFlingerSurfaceControl.clear();
- eglTerminate(mDisplay);
- IPCThreadState::self()->stopProcess();
- return r;
- }
这个函数流程比较简单,首先判断自定义的开机动画文件mZip是否存在,如果存在就调用movie()完成自定义开机画面的显示;如果不存在,调用android()完成系统默认开机画面的显示。然后进行开机动画显示后的销毁、释放工作,主要就是readyToRun中初始化的一些EGL对象。最后终止线程,并return。注意,movie()和android()的返回值都是false,因此线程结束也会返回false。threadLoop()函数如果返回值为false,则该函数中的内容只会执行一次;如果返回true,则会不停的执行。这里返回false,因此只会执行一次。
android()代码如下:
- bool BootAnimation::android()
- {
- 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();
- do {
- nsecs_t now = systemTime();
- double time = now - startTime;
- float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
- GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
- GLint x = xc - offset;
- glDisable(GL_SCISSOR_TEST);
- glClear(GL_COLOR_BUFFER_BIT);
- glEnable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
- glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glEnable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
- glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
- EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
- if (res == EGL_FALSE)
- break;
- // 12fps: don't animate too fast to preserve CPU
- const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
- if (sleepTime > 0)
- usleep(sleepTime);
- checkExit();
- } while (!exitPending());
- glDeleteTextures(1, &mAndroid[0].name);
- glDeleteTextures(1, &mAndroid[1].name);
- return false;
- }
首先,调用initTexture读取android系统默认的开机动画图片,生成纹理对象,并放在mAndroid数组中。开机动画图片共两张:android-logo-mask.png和android-logo-shine.png,存放在frameworks\base\core\res\assets\images路径下。其中android-logo-mask.png是黑底白字“android”字样;android-logo-shine.png是黑白渐变的显示背景。接着,clear screen。然后,在do...while循环中渲染两个纹理对象,从而生成开机动画。在循环语句最后会执行checkExit()函数。
- void BootAnimation::checkExit() {
- // Allow surface flinger to gracefully request shutdown
- char value[PROPERTY_VALUE_MAX];
- property_get(EXIT_PROP_NAME, value, "0");
- int exitnow = atoi(value);
- if (exitnow) {
- requestExit();
- if (mAudioPlayer != NULL) {
- mAudioPlayer->requestExit();
- }
- }
- }
首先调用property_get获取属性EXIT_PROP_NAME的值。
- #define EXIT_PROP_NAME "service.bootanim.exit"
然后判断该值,如果为1,则调用requestExit()要求退出当前线程,该函数是异步的。
回到android()代码:
- while (!exitPending());
这样,当属性“service.bootanim.exit”值被设为"1"时,android()就会调用requestExit(),exitPending()返回值为true。于是do...while()循环就会退出,开机动画绘制就会结束。
至于什么时候是哪个服务将属性“service.bootanim.exit”的值设置为1的,我们后面讲开机动画的停止的时候会提到。
下面分析一下movie()函数的具体实现。由于函数比较长,所以这里分段分析。
- String8 desString;
- 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;
- }
- }
- 480 800 30
- p 1 0 folder0
- p 0 10 folder1
第一行:前两个是开机动画在屏幕上显示的像素大小,分别为宽度和高度。第三个数字为每秒显示的帧数。
下面两行描述开机动画文件及显示的次数和间隔时间,每一行为一个片段。第一项类型,如p或c;第二项为该动画文件显示的次数,“0”表示重复显示;第三项为两次显示的间隔时间;第四项为动画文件。比如,"p 1 0 folder0"表示folder0文件夹中的动画只显示一次;”p 0 10 folder1“表示folder1中的动画文件重复显示,且两次显示的间隔时间为10秒。
- // Parse the description file
- 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 %d", &width, &height, &fps, &flg) >= 3) {
- //ALOGD("> w=%d, h=%d, fps=%d, flg=%d", width, height, fps, flg);
- 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;
- }
上面这段代码主要是解析上面读取的desc.txt,并将相关参数保存到Animation对象animation中。
- // 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);
上面这段代码是读取每个片段中的png图片,并保存在animation.parts.frames中。下面就开始将开机动画绘制到屏幕上,代码如下:
- // clear screen
- glShadeModel(GL_FLAT);
- glDisable(GL_DITHER);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
- 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);
- }<span style="white-space:pre"> </span>
- 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);
- }
- }
- }
- return false;
到此,开机动画的显示流程就完成了。下面,分析一下开机动画是怎么停止的。
3 开机动画的停止
当SystemServer将系统中的关键服务启动完成后,会启动桌面启动器Launcher。Launcher启动后,会向ActivityManagerService发送一个Activity组件空闲通知,AMS收到该通知后,就会调用成员函数enableScreenAfterBoot()停止开机动画,以便让屏幕显示桌面。enableScreenAfterBoot()代码如下:
- void enableScreenAfterBoot() {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
- mWindowManager.enableScreenAfterBoot();
- synchronized (this) {
- updateEventDispatchingLocked();
- }
- }
- public void enableScreenAfterBoot() {
- synchronized(mWindowMap) {
- ......
- if (mSystemBooted) {
- return;
- }
- mSystemBooted = true;
- hideBootMessagesLocked();
- // If the screen still doesn't come up after 30 seconds, give
- // up and turn it on.
- mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);
- }
- mPolicy.systemBooted();
- performEnableScreen();
- }
成员变量mSystemBooted用来标识系统是否已经启动,true代表启动,false代表未启动。这里,应该是false。因此,程序会继续往下执行,先把mSystemBooted设为true,然后调用performEnableScreen。代码如下:
- public void performEnableScreen() {
- synchronized(mWindowMap) {
- ......
- if (mDisplayEnabled) {
- return;
- }
- if (!mSystemBooted && !mShowingBootMessages) {
- return;
- }
- // Don't enable the screen until all existing windows have been drawn.
- if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
- return;
- }
- if (!mBootAnimationStopped) {
- // Do this one time.
- try {
- IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
- if (surfaceFlinger != null) {
- //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
- data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
- }
- mBootAnimationStopped = true;
- }
- ......
- }
SurfaceFlinger中处理该请求的函数为bootFinished。代码如下:
- void SurfaceFlinger::bootFinished()
- {
- const nsecs_t now = systemTime();
- const nsecs_t duration = now - mBootTime;
- ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
- mBootFinished = true;
- // wait patiently for the window manager death
- const String16 name("window");
- sp<IBinder> window(defaultServiceManager()->getService(name));
- if (window != 0) {
- window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
- }
- // stop boot animation
- // formerly we would just kill the process, but we now ask it to exit so it
- // can choose where to stop the animation.
- property_set("service.bootanim.exit", "1");
- }
这里注意:在android之前版本中,bootFinished不是设置属性“service.bootanim.exit”,而是调用:
- property_set("ctl.stop", "bootanim");
这样,init进程会直接kill掉bootanimation进程,从而结束开机动画的显示。
4 开机音乐的实现
使用MediaPlay实现,思路大致如下:
Makefile文件中添加:
- LOCAL_SHARED_LIBRARIES += \
- libmedia
- #include <system/audio.h>
- bool BootAnimation :: soundplay()
- {
- mfd = open(xxxxx, O_RDONLY); //xxxxx为音乐文件
- mp = new MediaPlayer();
- mp->setDataSource(mfd, 0, 0x7ffffffffffffffLL);
- mp->setAudioStreamType(/*AUDIO_STREAM_MUSIC*/AUDIO_STREAM_SYSTEM);
- mp->prepare();
- mp->start();
- }
- bool BootAnimation::soundstop()
- {
- if (mp != NULL)
- mp->stop();
- }