(1)动画文件的存在位置
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";
static const std::vector<std::string> bootFiles = {
APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE
};
static const std::vector<std::string> shutdownFiles = {
PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""
};
static const std::vector<std::string> userspaceRebootFiles = {
PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE, OEM_USERSPACE_REBOOT_ANIMATION_FILE,
SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE,
};
动画文件的读取是按顺序进行的,如果读取成功,则不再读取后续的文件,如果失败,则读取下一个文件。
加载动画包和配置文件(desc.txt)就不详细分析了。
加载动画执行完成之后,用来显示开机画面的线程的初始化工作就执行完成了,接下来就会执行这个线程的主体函数,即BootAnimation类的成员函数threadLoop。
(2)执行动画ThreadLoop
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
bool BootAnimation::threadLoop()
{
bool result;
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
result = android();
} else {
result = 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 result;
}
首先判断自定义的开机动画文件mZipFileName是否存在,如果存在就调用movie()完成自定义开机画面的显示;如果不存在,调用android()完成系统默认开机画面的显示。
movie()和android()的返回值都是false,因此线程结束也会返回false。threadLoop()函数如果返回值为false,则该函数中的内容只会执行一次;如果返回true,则会不停的执行。这里返回false,因此只会执行一次。
接下来简单看一下android()和movie()函数。
bool BootAnimation::android() {
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
// 这两张图片保存在frameworks/base/core/res/assets/images目录中,它们最终会被编译
// 在framework-res模块(frameworks/base/core/res)中,即编译在framework-res.apk文件中。
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
do {
//...
checkExit();
} while (!exitPending());
return false;
}
bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
if (mAnimation == nullptr)
return false;
}
playAnimation(*mAnimation);
releaseAnimation(mAnimation);
mAnimation = nullptr;
return false;
}
bool BootAnimation::playAnimation(const Animation& animation) {
if (part.animation != nullptr) {
playAnimation(*part.animation);
if (exitPending())
break;
continue; //to next part
}
if(exitPending() && !part.playUntilComplete)
break;
//...
checkExit();
if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
break;
}
在循环语句最后会执行checkExit()函数:
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
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();
mCallbacks->shutdown();
}
}
首先调用property_get获取属性EXIT_PROP_NAME的值,如果为1,则调用requestExit()要求退出当前线程,该函数是异步的。
//system/core/libutils/Thread.cpp
void Thread::requestExit()
{
Mutex::Autolock _l(mLock);
//这里将mExitPending 赋值为true
mExitPending = true;
}
bool Thread::exitPending() const
{
Mutex::Autolock _l(mLock);
return mExitPending;
}
动画执行时通过调用exitPending()检测,该函数判断requestExit()是否被调用过,如果调用过则返回true,否则为false。
当属性"service.bootanim.exit"值被设为"1"时,android()就会调用requestExit(),exitPending()返回值为true。于是循环就会退出,开机动画就会结束。
至于什么时候是哪个服务将属性"service.bootanim.exit"的值设置为1的,我们后面讨论开机动画的停止的时候会提到。
(3)开机动画包bootanimation.zip
每个压缩文件都必须包含有一个名称为"desc.txt"的文件,这是用来描述用户自定义的开机动画是如何显示的。
480 640 20
p 1 0 folder1
p 2 20 folder2
c 0 0 folder3
c 1 0 folder4
第1行用来描述开机动画在屏幕显示的大小及帧率。具体为:开机动画的宽度为480个像素,高度为640个像素,显示频率为每秒20帧,即每帧显示1/20秒。
下面的每一行代表一个片段,显示的时候会按照顺序从上到下依次显示。
- 第1个字符为片段类型,有’c’和’p’两种;
- 第2个数字为该片段重复显示的次数,如果为’0’,表示会无限重复显示;
- 第3个数字为两次显示之间的间隔,单位为第一行中定义的每帧显示的时间;
- 第4个字符串为该片段所在的文件夹,一个片段可以由多个png图片组成,都存放在folder文件夹中;
解释一下:
- “p 1 0 folder1”——代表该片段显示1次,与下一个片段间隔0s,该片段的显示图片路径为bootanimation.zip/folder1;
- “p 2 20 folder2”——代表该片段显示2次,且两次之间显示的间隔为20(1/20)=1s,与下一个片段间隔20(1/20)=1s,该片段的显示图片路径为bootanimation.zip/folder2;
- “c 0 0 folder3”——代表该片段无限循环显示,且两次显示的间隔为0s,与下一个片段间隔0s,该片段的显示图路径为bootanimation.zip/folder3;
- “c 1 10 folder4”——代表该片段显示1次,显示后暂停10*(1/20)=0.5s,该片段的显示图路径为bootanimation.zip/folder4;
总结:
如果exitPending()返回值为true且part.playUntilComplete=false,则会break。即:当SurfaceFlinger服务要求bootanimation停止显示动画时,以’p’标识的片段会停止,而以’c’标识的片段会继续显示。这就是两者之间的主要区别。
'c’标识的意思应该是continue,即:即使SurfaceFlinger要求bootanimation停止动画,bootanimation也不会立刻停止动画,它会等c标识片段都显示完毕后,再停止。