1. 谁拉起了开机动画
在系统上电后,通过内置的rom_code将uboot映像从flash中加载到内存中运行,之后跳转到uboot执行,做一些硬件外设参数的初始化工作,然后从flash中加载kernel到内存中运行并跳转到kernel执行。
init进程是kernel的第一个进程,也是Android系统的第一个进程。而跟开机动画有关的,主要是init中做的两件事,一件事是启动surfaceflinger进程,开机动画的启动就是在这里触发;另外一件事是启动zygote进程,zygote进程起来后就fork出了system_server,system_server主要是启动系统服务,如AMS,WMS,PMS等。等待一些关键服务ready后,就开始加载launcher,launcher加载好就触发结束开机动画的操作,从而进入到launcher界面。
开机动画是在surfaceflinger中触发,而surfaceflinger进程是在init.rc中启动的,如下:
1 service surfaceflinger /system/bin/surfaceflinger
需要注意的是高版本的Android上,如AndroidP,surfaceflinger进程并不是直接在init.rc文件中启动的,而是通过Android.bp文件去包含启动surfaceflinger.rc文件,然后在该文件中再去启动surfaceflinger:
1 2 3 4 5 6 7 8 9 10 11 # frameworks/native/services/surfaceflinger/Android.bp cc_binary { name: "surfaceflinger", defaults: ["surfaceflinger_defaults"], init_rc: ["surfaceflinger.rc"], # 编译的时候将启动脚本放在surfaceflinger.rc srcs: ["main_surfaceflinger.cpp"], # 启动之后,会调用main_surfaceflinger的main方法 whole_static_libs: [ "libsigchain", ], ...... }
1 2 3 4 5 6 7 8 9 10 # frameworks/native/services/surfaceflinger/surfaceflinger.rc service surfaceflinger /system/bin/surfaceflinger class core animation user system group graphics drmrpc readproc onrestart restart zygote writepid /dev/stune/foreground/tasks socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0 socket pdx/system/vr/display/vsync stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
1 2 # system/etc/init/surfaceflinger.rc # 系统打出来之后,rc文件会放在system/etc/init目录,init进程启动的时候,会自动解析该目录的rc文件,并且启动对应的服务。
1 2 3 4 5 6 7 8 9 10 11 int main (int , char **) { ... sp<SurfaceFlinger> flinger = new SurfaceFlinger(); ... flinger->init(); ... flinger->run(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void SurfaceFlinger::init () { ...... if (getHwComposer().hasCapability( HWC2::Capability::PresentFenceIsNotReliable)) { mStartPropertySetThread = new StartPropertySetThread(false ); } else { mStartPropertySetThread = new StartPropertySetThread(true ); } if (mStartPropertySetThread->Start() != NO_ERROR) { ALOGE("Run StartPropertySetThread failed!" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 namespace android {StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue): Thread(false ), mTimestampPropertyValue(timestampPropertyValue) {} status_t StartPropertySetThread::Start () { return run("SurfaceFlinger::StartPropertySetThread" , PRIORITY_NORMAL); } bool StartPropertySetThread::threadLoop () { property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0" ); property_set("service.bootanim.exit" , "0" ); property_set("ctl.start" , "bootanim" ); return false ; } }
2. Bootanimation实现流程 1 2 3 4 5 6 7 8 // frameworks/base/cmds/bootanimation/bootanim.rc service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio disabled oneshot writepid /dev/stune/top-app/tasks
通过SurfaceFlinger启动开机动画进程。调用bootanimation_main的main方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # frameworks/base/cmds/bootanimation/bootanimation_main.cpp int main () { setpriority(PRIO_PROCESS, 0 , ANDROID_PRIORITY_DISPLAY); bool noBootAnimation = bootAnimationDisabled(); ALOGI_IF(noBootAnimation, "boot animation disabled" ); if (!noBootAnimation) { sp<ProcessState> proc (ProcessState::self()) ; ProcessState::self()->startThreadPool(); waitForSurfaceFlinger(); sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks()); ALOGV("Boot animation set up. Joining pool." ); IPCThreadState::self()->joinThreadPool(); } ALOGV("Boot animation exit" ); return 0 ; }
new了一个BootAnimation实例,然后创建了一个binder线程池,用于显示动画时,与surfaceflinger进程通信用。接下来看看BootAnimation的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 BootAnimation::BootAnimation(sp<Callbacks> callbacks) : Thread(false ), mClockEnabled(true ), mTimeIsAccurate(false ), mTimeFormat12Hour(false ), mTimeCheckThread(NULL ), mCallbacks(callbacks) { mSession = new SurfaceComposerClient(); std ::string powerCtl = android::base::GetProperty("sys.powerctl" , "" ); if (powerCtl.empty()) { mShuttingDown = false ; } else { mShuttingDown = true ; } }
这里创建了SurfaceComposerClient,用于与surfaceflinger通讯。接下来就到了onFirstRef:
1 2 3 4 5 6 7 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); } }
这里先注册surfaceflinger的死亡消息通知书linkToComposerDeath
,只要surfaceflinger挂掉了,bootanimation进程就会收到通知,从而执行如下代码:
1 2 3 4 5 6 7 8 9 10 void BootAnimation::binderDied(const wp<IBinder>&) { // woah, surfaceflinger died! ALOGD("SurfaceFlinger died, exiting..."); // calling requestExit() is not enough here because the Surface code // might be blocked on a condition variable that will never be updated. kill( getpid(), SIGKILL ); requestExit(); }
直接退出,等待surfaceflinger的下一次重启,如果还有来生的话。
onFirstRef在创建了死亡通知书后,还做了一件事,那就是run bootanimation,个中细节不在这里列出,bootanimation重写了readyToRun和threadLoop,我们直接看threadLoop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bool BootAnimation::threadLoop () { bool r; if (mZipFileName.isEmpty()) { r = android(); } 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); eglReleaseThread(); IPCThreadState::self()->stopProcess(); return r; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bool BootAnimation::movie () { Animation* animation = loadAnimation(mZipFileName); if (animation == NULL ) return false ; ..... playAnimation(*animation); ...... releaseAnimation(animation); ...... return false ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 bool BootAnimation::playAnimation (const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1 ) / animation.fps; const int animationX = (mWidth - animation.width) / 2 ; const int animationY = (mHeight - animation.height) / 2 ; 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 ); if (part.animation != NULL ) { playAnimation(*part.animation); if (exitPending()) break ; continue ; } for (int r=0 ; !part.count || r<part.count ; r++) { ...... checkExit(); } ..... } } ...... return true ; }
1 2 3 4 5 6 7 8 9 10 11 12 void BootAnimation::checkExit () { char value[PROPERTY_VALUE_MAX]; property_get(EXIT_PROP_NAME, value, "0" ); int exitnow = atoi(value); if (exitnow) { requestExit(); mCallbacks->shutdown(); } }
至此,从每一帧开机动画播完之后,都会检测service.bootanim.exit
的值,当属性值为1的时候,则开机动画会requestExit, 从而结束开机动画。那是谁给service.bootanim.exit
的属性值设置为1呢?
3.bootanimation的结束 init启动zygote进程之后,由zygote孵化出了system_server,然后system_server启动了各种各种的系统所需的服务,其中就有AMS,AMS启动并ready后,会执行startHomeActivityLocked:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void systemReady (final Runnable goingCallback, TimingsTraceLog traceLog) { ... startHomeActivityLocked(currentUserId, "systemReady" ); ... } boolean startHomeActivityLocked (int userId, String reason) { ... Intent intent = getHomeIntent(); ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId); final String myReason = reason + ":" + userId + ":" + resolvedUserId; mActivityStartController.startHomeActivity(intent, aInfo, myReason); ... }
launcher在这里开始加载启动,在launcher的主线程处于空闲时,就会向ActivityManagerService发送一个activityIdle的消息:
1 2 3 4 5 6 7 public final void activityIdle (IBinder token, Configuration config, boolean stopProfiling) { ... ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token, false , false , config); ... }
这里通过activityIdleInternalLocked获取到ActivityRecord实例,我们看看具体实现:
1 2 3 4 5 6 7 8 9 10 11 final ActivityRecord activityIdleInternalLocked (final IBinder token, boolean fromTimeout, boolean processPausingActivities, Configuration config) { .... if (isFocusedStack(r.getStack()) || fromTimeout) { booting = checkFinishBootingLocked(); } .... }
这里会检测开机是否结束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private boolean checkFinishBootingLocked () { final boolean booting = mService.mBooting; boolean enableScreen = false ; mService.mBooting = false ; if (!mService.mBooted) { mService.mBooted = true ; enableScreen = true ; } if (booting || enableScreen) { mService.postFinishBooting(booting, enableScreen); } return booting; }
这里会直接进入到postFinishBooting方法中执行:
1 2 3 4 5 void postFinishBooting (boolean finishBooting, boolean enableScreen) { mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG, finishBooting ? 1 : 0 , enableScreen ? 1 : 0 )); }
这里直接发一条消息FINISH_BOOTING_MSG
,我们看看具体handler的处理:
1 2 3 4 5 6 7 8 9 10 case FINISH_BOOTING_MSG: { if (msg.arg1 != 0 ) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting" ); finishBooting(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } if (msg.arg2 != 0 ) { enableScreenAfterBoot(); } break ;
最终执行到了enableScreenAfterBoot方法:
1 2 3 4 5 void enableScreenAfterBoot () { ... mWindowManager.enableScreenAfterBoot(); ... }
这里调用了WMS的方法enableScreenAfterBoot
,我们跳入看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void enableScreenAfterBoot () { ... performEnableScreen(); ... } private void performEnableScreen () { ... if (!mBootAnimationStopped) { Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim" , 0 ); SystemProperties.set("service.bootanim.exit" , "1" ); mBootAnimationStopped = true ; } ... }
到了这里,最终通过设置service.bootanim.exit
的值,stop掉了开机动画,接着来的就是发出了开机广播。
4.开机动画优化 由于当前项目的开机动画由QNX来控制,因此,我们尽量减轻Android开机动画的播放就行。
4.1 分析描述文件 优化前的配置文件
1 2 3 4 5 6 7 832 520 30 c 1 30 part0 c 1 0 part1 c 0 0 part2 c 1 30 part3 c 1 0 part4 c 1 0 part5
第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。
剩余的每一行都用来描述一个动画片断,p
这个flag表示某一个part的图片的处理方式,bootanimation 程序在处理某part的图片时会参考这个标志.P表示bootanimation可以随时退出当处理完一张图片后,如果bootanimation可以退出. ”C” 则表示必须把这部分的图片处理完毕才可以退出.事实上这个标识只要不为C, 则其行为完全一致.
“1”这个flag表示这part的图片需要循环处理多少次. 1表示处理循环处理一次.”0” 表示无限循环.
“0” 这个flag 表示播放完这部分所有图片后需要pause多久时间。”0” 表示不需要pause。“part0”表示动画的第一部分内容。“part1” 表示动画的第二部分内容。
开始处理新的一帧, 如果这部分part的处理标识不是”c”则可以退出.否则不能退出. 处理完一次循环后, 如果这部分part的处理标识是”c”,并且这部分是无限循环,则可以退出.如果不是无限循环则不能退出, 需要完全处理完这部分的图片才可能退出.
优化后的配置文件
然后将资源包放在车机:/system/media/bootanimation.zip
原生的开机动画路径:packages/services/Car/car_product/bootanimations/bootanimation.zip
5.总结 优化开机动画主要原理是开机动画会比较消耗资源,在开机启动服务期间减少开机动画对CPU的消耗,从侧面就提升了Android的启动耗时。