一步一图项目上要加一个开机动画结束的回调,我这边看下如何加
好,老规矩,如何启动动画?动画是谁启动的?怎么关闭的?谁通知关闭的
带着问题看源码
动画的启动流程
开机动画的主入口在哪?
这个main.cpp就是主入口
那么是谁通知它启动的?
Android系统在启动SystemServer进程时,通过两个阶段来启动系统所有服务,在第一阶段启动本地服务,如SurfaceFlinger,SensorService等,在第二阶段则启动一系列的Java服务。开机动画是在什么时候启动的呢?通过查看源码,Android开机动画是在启动SurfaceFlinger服务时启动的。SystemServer的main函数首先调用init1来启动本地服务,init1函数通过JNI调用C语言中的system_init()函数来实现服务启动。
那么当SurfaceFliger启动之后 会调用init函数
void SurfaceFlinger::init()
{
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
#ifdef DDMS_DEBUGGING
property_get("debug.sf.ddms", value, "0");
mDebugDDMS = atoi(value);
if (mDebugDDMS) {
DdmConnection::start(getServiceName());
}
#endif
property_get("ro.bootmode", value, "mode");
if (!(strcmp(value, "engtest")
&& strcmp(value, "special")
&& strcmp(value, "wdgreboot")
&& strcmp(value, "unknowreboot")
&& strcmp(value, "panic"))) {
SurfaceFlinger::sBootanimEnable = false;
}
}
然后一系列调用,当surfaceFliger 先启动一个线程,进行初始化然后当初始化结束之后调用 startBootAnim
int Thread::_threadLoop(void* user)
{
省略一车代码
Thread* const self = static_cast<Thread*>(user);
self->mStatus = self->readyToRun();
}
status_t SurfaceFlinger::readyToRun()
{
初始化代码,本次不关心
mReadyToRunBarrier.open();
// start boot animation
startBootAnim();
return NO_ERROR;
}
当显示系统初始化完毕后,调用startBootAnim()函数来显示开机动画。
void SurfaceFlinger::startBootAnim() {
// start boot animation
if(SurfaceFlinger::sBootanimEnable){
property_set("service.bootanim.exit", "0");
property_set("ctl.start", "bootanim");
}
}
该进程对应的源码位于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) {
//启动Binder线程池,用于接收其他进程的请求
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the boot animation object (may take up to 200ms for 2MB zip)
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
waitForSurfaceFlinger();
boot->run("BootAnimation", PRIORITY_DISPLAY);
ALOGV("Boot animation set up. Joining pool.");
//将当前线程注册到Binder线程池中
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
在构造BootAnimation对象时,会调用onFirstRef函数。
void BootAnimation::onFirstRef() {
if (err == NO_ERROR) {
省略无用代码
preloadAnimation();
}
}
先选择需要播放的动画,然后播放
播放路径有很多,这地方很简单,感兴趣可以看一下
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
上面是路径
bool BootAnimation::preloadAnimation() {
选择路径 会循环直到找到,如果没有我理解会加载一个默认值
findBootAnimationFile();
if (!mZipFileName.isEmpty()) {
播放动画
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}
return false;
}
该函数首先为SurfaceComposerClient对象注册Binder死亡通知,然后调用BootAnimation的run方法,由于BootAnimation同时继承于Thread类,前面介绍SurfaceFlinger时已经介绍到,当某个类继承于Thread类时,当调用该类的run函数时,函数首先会执行readyToRun()函数来完成线程执行前的一些工作,然后线程反复执行threadLoop()函数,在BootAnimation类中,同样重新了这两个方法
bool BootAnimation::threadLoop()
{
bool r;
//如果mAndroidAnimation为true,表示动画文件不存在,则显示Android滚动字样
if (mAndroidAnimation) {
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);
IPCThreadState::self()->stopProcess();
return r;
}
开机画面主要是由一个zip格式的压缩包bootanimation.zip组成,压缩包里面包含数张png格式的图片,还有一个desc.txt的文本文档,开机时按desc.txt里面的指令,屏幕上会按文件名称顺序连续的播放一张张的图片,就像播放原始的胶带影片一样,形成动画。desc.txt是一个保存形式为ANSI格式的文件,用于设置这个动画像素(大小),帧数,闪烁次数,文件夹名称等。内容如下:
480 854 10
p 1 2 folder1
p 0 2 folder2
课代表总结,SurfaceFliger在初始化第一轮由init.rc启动,初始化结束然后进行启动动画播放,新起一个线程循环播放我们的动画,按顺序获取动画路径,也就是可以做拦截
OK
那么看下结束流程
一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。
由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。
在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。
结束流程是一堆调用链,所以总结下
结束是Activity idle机制进行 也就是主线程空闲通知ATP 一堆调用链先放开机广播,然后进行调用让SurfaceFliger通知动画结束最后通知AMS
如何关闭动画的?
动画是启动了一个线程进行循环播放,关闭就是中断了这个线程,结束掉播放