一步一图项目上要加一个开机动画结束的回调,我这边看下如何加
好,老规矩,如何启动动画?动画是谁启动的?怎么关闭的?谁通知关闭的
带着问题看源码
动画的启动流程
开机动画的主入口在哪?
这个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_DEBUGGINGproperty_get("debug.sf.ddms", value, "0");mDebugDDMS = atoi(value);if (mDebugDDMS) {DdmConnection::start(getServiceName());}
#endifproperty_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 animationstartBootAnim();return NO_ERROR;
}
当显示系统初始化完毕后,调用startBootAnim()函数来显示开机动画。
void SurfaceFlinger::startBootAnim() {// start boot animationif(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
如何关闭动画的?
动画是启动了一个线程进行循环播放,关闭就是中断了这个线程,结束掉播放