Android6.0 开机动画(一) bootanim启动

转自 http://blog.csdn.net/kc58236582/article/details/52794846

这个老兄的博客 写的比较多的android6.0 相关的文章,值得去看看,现在android6.0 以上的文章太少了。

开机动画是在进程bootanim,我们来看下init.rc中文件,

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. service bootanim /system/bin/bootanimation boot  
  2.     class core  
  3.     user graphics  
  4.     group graphics audio  
  5.     disabled  
  6.     oneshot  

 应用程序bootanimation的用户和用户组名称分别被设置为graphics。注意, 用来启动应用程序bootanimation的服务是disable的,即init进程在启动的时候,不会主动将应用程序bootanimation启动起来。当SurfaceFlinger服务启动的时候,它会通过修改系统属性ctl.start的值来通知init进程启动应用程序bootanimation,以便可以显示第三个开机画面,而当System进程将系统中的关键服务都启动起来之后,ActivityManagerService服务就会通知SurfaceFlinger服务来修改系统属性ctl.stop的值,以便可以通知init进程停止执行应用程序bootanimation来显示开机画面。

在之前分析SurfaceFlinger的博客中,在init函数最后会调用startBootAnim函数

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void SurfaceFlinger::init() {  
  2.     ......  
  3.     startBootAnim();  
  4. }  
而startBootAnim函数后面会设置属性ctl.start"为bootanim,这样bootanim进程就会启动。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void SurfaceFlinger::startBootAnim() {  
  2.     // start boot animation  
  3.     property_set("service.bootanim.exit""0");  
  4.     property_set("ctl.start""bootanim");  
  5. }  


一、bootanim进程

下面我们来看下bootanim的main函数

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int main(int argc,char **argv)  
  2. {  
  3.     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);  
  4.   
  5.     char value[PROPERTY_VALUE_MAX];  
  6.     property_get("debug.sf.nobootanimation", value, "0");  
  7.   
  8.     if (strcmp(basename(argv[1]),"shutdown"))  
  9.         bootOrshutdown = 0;  
  10.     else  
  11.         bootOrshutdown = 1;  
  12.   
  13.     int noBootAnimation = atoi(value);  
  14.     ALOGI_IF(noBootAnimation,  "boot animation disabled");  
  15.     if (!noBootAnimation) {  
  16.   
  17.         sp<ProcessState> proc(ProcessState::self());  
  18.         ProcessState::self()->startThreadPool();  
  19.   
  20.         // create the boot animation object  
  21.         sp<BootAnimation> boot = new BootAnimation();  
  22.   
  23.         IPCThreadState::self()->joinThreadPool();  
  24.   
  25.     }  
  26.     return 0;  
  27. }  

这里先会判断是开机还是关机,然后创建BootAnimation对象。

我们先来看下构造函数和onFirstRef函数,构造函数中创建了一个SurfaceComposerClient对象,onFirstRef启动了线程。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. BootAnimation::BootAnimation() : Thread(false), mZip(NULL)  
  2. {  
  3.     mSession = new SurfaceComposerClient();  
  4. }  
  5.   
  6. void BootAnimation::onFirstRef() {  
  7.     status_t err = mSession->linkToComposerDeath(this);  
  8.     ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));  
  9.     if (err == NO_ERROR) {  
  10.         run("BootAnimation", PRIORITY_DISPLAY);  
  11.     }  
  12. }  

readyToRun函数是在线程启动时调用的。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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.   
  11.     // create the native surface  
  12.     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),  
  13.             dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);  
  14.   
  15.     SurfaceComposerClient::openGlobalTransaction();  
  16.     control->setLayer(0x40000000);  
  17.     SurfaceComposerClient::closeGlobalTransaction();  
  18.   
  19.     sp<Surface> s = control->getSurface();  
  20.   
  21.     // initialize opengl and egl  
  22.     const EGLint attribs[] = {  
  23.             EGL_RED_SIZE,   8,  
  24.             EGL_GREEN_SIZE, 8,  
  25.             EGL_BLUE_SIZE,  8,  
  26.             EGL_DEPTH_SIZE, 0,  
  27.             EGL_NONE  
  28.     };  
  29.     EGLint w, h;  
  30.     EGLint numConfigs;  
  31.     EGLConfig config;  
  32.     EGLSurface surface;  
  33.     EGLContext context;  
  34.   
  35.     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);  
  36.   
  37.     eglInitialize(display, 0, 0);  
  38.     eglChooseConfig(display, attribs, &config, 1, &numConfigs);  
  39.     surface = eglCreateWindowSurface(display, config, s.get(), NULL);  
  40.     context = eglCreateContext(display, config, NULL, NULL);  
  41.     eglQuerySurface(display, surface, EGL_WIDTH, &w);  
  42.     eglQuerySurface(display, surface, EGL_HEIGHT, &h);  
  43.   
  44.     if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)  
  45.         return NO_INIT;  
  46.   
  47.     mDisplay = display;  
  48.     mContext = context;  
  49.     mSurface = surface;  
  50.     mWidth = w;  
  51.     mHeight = h;  
  52.     mFlingerSurfaceControl = control;  
  53.     mFlingerSurface = s;  
  54.   
  55.     ZipFileRO* zipFile = NULL;  
  56.     float volume;  
  57.     const char * file;  
  58.     if(1 == bootOrshutdown) {  
  59.         volume = 1.0;  
  60.         zipFile = ZipFileRO::open("/system/media/shutdownanimation.zip");  
  61.         file = "/system/media/shutdown.mp3";  
  62.     } else {  
  63.         volume = 0.4;  
  64.         zipFile = ZipFileRO::open("/system/media/bootanimation.zip");  
  65.         file = "/system/media/boot.mp3";  
  66.     }  
  67.     mZip = zipFile;  
  68.     #ifndef OPT_PROJ_TARGET_CUCC  
  69.         mp = new MediaPlayer();  
  70.         sp<IMediaHTTPService> bootmedia=NULL ;  
  71.         if (mp->setDataSource(bootmedia,file,NULL) == NO_ERROR) {  
  72.             mp->setAudioStreamType(AUDIO_STREAM_MUSIC);  
  73.             mp->prepare();  
  74.             mp->setVolume(volume, volume);  
  75.             mp->setListener(new BootMediaPlayerListener());  
  76.         } else {  
  77.             ALOGE("Failed to load Bootanimation sounds: %s", file);  
  78.         }  
  79.     #endif  
  80.     return NO_ERROR;  
  81. }  

   BootAnimation类的成员函数session用来返回BootAnimation类的成员变量mSession所描述的一个SurfaceComposerClient对象。通过调用SurfaceComposerClient对象mSession的成员函数createSurface可以获得一个SurfaceControl对象control。

        SurfaceComposerClient类的成员函数createSurface首先调用内部的Binder代理对象mClient来请求SurfaceFlinger返回一个类型为SurfaceLayer的Binder代理对象,接着再使用这个Binder代理对象来创建一个SurfaceControl对象。创建出来的SurfaceControl对象的成员变量mSurface就指向了从SurfaceFlinger返回来的类型为SurfaceLayer的Binder代理对象。有了这个Binder代理对象之后,SurfaceControl对象就可以和SurfaceFlinger服务通信了。

       调用SurfaceControl对象control的成员函数getSurface会返回一个Surface对象s。这个Surface对象s内部也有一个类型为SurfaceLayer的Binder代理对象mSurface,这个Binder代理对象与前面所创建的SurfaceControl对象control的内部的Binder代理对象mSurface引用的是同一个SurfaceLayer对象。这样,Surface对象s也可以通过其内部的Binder代理对象mSurface来和SurfaceFlinger服务通信。

       Surface类继承了ANativeWindow类。ANativeWindow类是连接OpenGL和Android窗口系统的桥梁,即OpenGL需要通过ANativeWindow类来间接地操作Android窗口系统。这种桥梁关系是通过EGL库来建立的,所有以egl为前缀的函数名均为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来获得。


下面我们再看下threadLoop函数:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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.         AudioSystem::getDeviceConnectionState(0x4, "AudioPolicyThreadStart");  
  10.         usleep(100*1000);  
  11.         #ifndef OPT_PROJ_TARGET_CUCC  
  12.             mp->start();  
  13.         #endif  
  14.         r = movie();  
  15.     }  
  16.     ALOGD("add step to know whether leave the movie");  
  17.     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);  
  18.     eglDestroyContext(mDisplay, mContext);  
  19.     eglDestroySurface(mDisplay, mSurface);  
  20.     mFlingerSurface.clear();  
  21.     mFlingerSurfaceControl.clear();  
  22.     eglTerminate(mDisplay);  
  23.     IPCThreadState::self()->stopProcess();  
  24.     ALOGD("add step to know whether finish all the steps");  
  25.     return r;  
  26. }  

当mZip为空就用android原生的启动画面,如果不是就用自定义的。

android方法是播放原生的,movie是播放自定义的。

我们看下android方法

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool BootAnimation::android()  
  2. {  
  3.     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");  
  4.     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");  
  5.   
  6.     // clear screen  
  7.     glShadeModel(GL_FLAT);  
  8.     glDisable(GL_DITHER);  
  9.     glDisable(GL_SCISSOR_TEST);  
  10.     glClearColor(0,0,0,1);  
  11.     glClear(GL_COLOR_BUFFER_BIT);  
  12.     eglSwapBuffers(mDisplay, mSurface);  
  13.   
  14.     glEnable(GL_TEXTURE_2D);  
  15.     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  
  16.   
  17.     const GLint xc = (mWidth  - mAndroid[0].w) / 2;  
  18.     const GLint yc = (mHeight - mAndroid[0].h) / 2;  
  19.     const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);  
  20.   
  21.     glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),  
  22.             updateRect.height());  
  23.   
  24.     // Blend state  
  25.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  
  26.     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  
  27.   
  28.     const nsecs_t startTime = systemTime();  
  29.     do {  
  30.         nsecs_t now = systemTime();  
  31.         double time = now - startTime;  
  32.         float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;  
  33.         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;  
  34.         GLint x = xc - offset;  
  35.   
  36.         glDisable(GL_SCISSOR_TEST);  
  37.         glClear(GL_COLOR_BUFFER_BIT);  
  38.   
  39.         glEnable(GL_SCISSOR_TEST);  
  40.         glDisable(GL_BLEND);  
  41.         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);  
  42.         glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);  
  43.         glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);  
  44.   
  45.         glEnable(GL_BLEND);  
  46.         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);  
  47.         glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);  
  48.   
  49.         EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);  
  50.         if (res == EGL_FALSE)  
  51.             break;  
  52.   
  53.         // 12fps: don't animate too fast to preserve CPU  
  54.         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);  
  55.         if (sleepTime > 0)  
  56.             usleep(sleepTime);  
  57.   
  58.         checkExit();  
  59.     } while (!exitPending());  
  60.   
  61.     glDeleteTextures(1, &mAndroid[0].name);  
  62.     glDeleteTextures(1, &mAndroid[1].name);  
  63.     return false;  
  64. }  


二、WMS通知关闭开机动画

当System进程将系统中的关键服务启动起来之后,就会将应用程序启动器(Launcher)启动起来。 一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。

最终会调用AMS的enableScreenAfterBoot方法

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void enableScreenAfterBoot() {  
  2.     EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,  
  3.             SystemClock.uptimeMillis());  
  4.     mWindowManager.enableScreenAfterBoot();  
  5.   
  6.     synchronized (this) {  
  7.         updateEventDispatchingLocked();  
  8.     }  
  9. }  
在WMS的enableScreenAfterBoot方法中又会调用performEnableScreen方法
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void enableScreenAfterBoot() {  
  2.     synchronized(mWindowMap) {  
  3.         if (DEBUG_BOOT) {  
  4.             RuntimeException here = new RuntimeException("here");  
  5.             here.fillInStackTrace();  
  6.             Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled  
  7.                     + " mForceDisplayEnabled=" + mForceDisplayEnabled  
  8.                     + " mShowingBootMessages=" + mShowingBootMessages  
  9.                     + " mSystemBooted=" + mSystemBooted, here);  
  10.         }  
  11.         if (mSystemBooted) {  
  12.             return;  
  13.         }  
  14.         mSystemBooted = true;  
  15.         hideBootMessagesLocked();  
  16.         // If the screen still doesn't come up after 30 seconds, give  
  17.         // up and turn it on.  
  18.         mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);  
  19.     }  
  20.   
  21.     mPolicy.systemBooted();  
  22.   
  23.     performEnableScreen();  
  24. }  
WMS的performEnableScreen方法,会通过Binder调用SurfaceFlinger的transact的FIRST_CALL_TRANSACTION
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void performEnableScreen() {  
  2.     synchronized(mWindowMap) {  
  3.         if (mDisplayEnabled) {  
  4.             return;  
  5.         }  
  6.         if (!mSystemBooted && !mShowingBootMessages) {  
  7.             return;  
  8.         }  
  9.   
  10.         // Don't enable the screen until all existing windows have been drawn.  
  11.         if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {  
  12.             return;  
  13.         }  
  14.   
  15.         if (!mBootAnimationStopped) {  
  16.             // Do this one time.  
  17.             try {  
  18.                 IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");  
  19.                 if (surfaceFlinger != null) {  
  20.                     //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");  
  21.                     Parcel data = Parcel.obtain();  
  22.                     data.writeInterfaceToken("android.ui.ISurfaceComposer");  
  23.                     surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED  
  24.                             data, null, 0);  
  25.                     data.recycle();  
  26.                 }  
  27.             }  
最后会在SurfaceFlinger的bootFinshed函数中设置service.bootanim.exit属性为1,这个后面会决定bootanim进程什么时候关闭。
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void SurfaceFlinger::bootFinished()  
  2. {  
  3.     const nsecs_t now = systemTime();  
  4.     const nsecs_t duration = now - mBootTime;  
  5.     ALOGI("Boot is finished (%ld ms)"long(ns2ms(duration)) );  
  6.     mBootFinished = true;  
  7.   
  8.     // wait patiently for the window manager death  
  9.     const String16 name("window");  
  10.     sp<IBinder> window(defaultServiceManager()->getService(name));  
  11.     if (window != 0) {  
  12.         window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));  
  13.     }  
  14.   
  15.     // stop boot animation  
  16.     // formerly we would just kill the process, but we now ask it to exit so it  
  17.     // can choose where to stop the animation.  
  18.     property_set("service.bootanim.exit""1");  
  19.     char boot_exit_value[32];  
  20.     property_get("service.bootanim.exit", boot_exit_value, "0");  
  21.     ALOGD("The service.bootanim.exit property value is %d", atoi(boot_exit_value));  
  22. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值