Android 开机动画是一个由init进程触发、 SurfaceFlinger服务启动、 bootanimation进程负责播放的系统服务。它的生命周期可以被清晰地分为三个阶段:启动触发、动画播放、结束退出。
启动触发:由SurfaceFlinger服务拉起
- 服务配置 :开机动画被定义为一个名为
bootanim的服务,其配置文件是bootanim.rc。这个服务被标记为disabled和oneshot,意味着它不会开机自启,且退出后不会重启。
sql
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics
disabled
oneshot
- 核心启动者 :真正触发动画启动的,是负责图形合成的核心系统服务
SurfaceFlinger。SurfaceFlinger在启动过程中,会通过一个专门的线程 (StartPropertySetThread) 设置系统属性ctl.start为bootanim,从而触发init进程去拉起bootanim服务,并同时设置service.bootanim.exit属性为0,标记动画从头开始启动。
arduino
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::init() {
...
//创建StartPropertySetThread
mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
//启动 `StartPropertySetThread`,触发开机动画
if (mStartPropertySetThread->Start() != NO_ERROR) {
}
}
//frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
status_t StartPropertySetThread::Start() {
//`run()` 方法启动新线程后,新线程的入口函数最终会循环调用 `threadLoop()`
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
bool StartPropertySetThread::threadLoop() {
//设置时间戳属性,向其他服务同步 SurfaceFlinger 的状态。
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
//清除开机动画退出标志,确保开机动画能正确从头开始启动
property_set("service.bootanim.exit", "0");
//将动画进度归零(`"0"` 表示刚开始或未加载),确保开机动画能正确从头开始启动
property_set("service.bootanim.progress", "0");
//向 init 进程发送命令,启动开机动画服务
property_set("ctl.start", "bootanim");
//立即退出线程
return false;
}
动画播放:bootanimation进程的工作
- 程序入口 :
bootanim服务启动后,会执行/system/bin/bootanimation程序,其main()函数位于bootanimation_main.cpp。
scss
//frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main()
{
//设置进程优先级,0,作用于当前进程,优先级 `-4`(比普通进程高)
//`-4` 意味着动画进程比普通应用(nice=0)获得更多 CPU 时间片
//但内核仍然采用 CFS(完全公平调度器),不会完全抢占实时进程
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
//检查是否禁用动画
bool noBootAnimation = bootAnimationDisabled();
//如果禁用,`return 0`,进程立即退出
if (!noBootAnimation) {
//创建ProcessState
//打开binder驱动,映射一块内存用于 Binder通信
sp<ProcessState> proc(ProcessState::self());
//启动 Binder 线程池
ProcessState::self()->startThreadPool();
//工厂模式创建音频回调对象
//分配 BootAnimation 内存
//初始化 BootAnimation 成员
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
//等待 SurfaceFlinger
//bootanimation启动时,SurfaceFlinger 可能还未完成初始化
//如果在 SurfaceFlinger 未就绪时创建 Surface,会返回 `nullptr`
waitForSurfaceFlinger();
//启动动画线程,新线程创建完成,立即开始执行 `threadLoop()`
boot->run("BootAnimation", PRIORITY_DISPLAY);
//主线程进入 Binder 循环
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
//frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
return init(kDefaultDriver, false /*requireDefault*/);
}
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{
//no_destroy 告诉编译器不要在程序退出时调用析构函数
//这两个变量在程序启动时分配内存(在 `main()` 之前)
//存储在 `.bss` 段(未初始化数据段)
//生命周期贯穿整个进程
[[clang::no_destroy]] static sp<ProcessState> gProcess;
[[clang::no_destroy]] static std::mutex gProcessMutex;
//如果传入为空,表示只获取已初始化的实例
if (driver == nullptr) {
std::lock_guard<std::mutex> l(gProcessMutex);
return gProcess;
}
[[clang::no_destroy]] static std::once_flag gProcessOnce;
//**`std::call_once` 机制:多个线程同时调用,只有第一个线程执行 Lambda ,
//即使多次调用 `init()`,Lambda 也只执行第一次,
//使用原子操作和内存屏障,无需外部锁 |
//如果 Lambda 抛出异常,`once_flag` 重置,允许重试
std::call_once(gProcessOnce, [&](){
//检查是否有读权限,如果指定驱动不可用,自动降级到通用 Binder
if (access(driver, R_OK) == -1) {
driver = "/dev/binder";
}
//std::lock_guard 保护 `gProcess` 的赋值操作(防止内存可见性问题)
std::lock_guard<std::mutex> l(gProcessMutex);
//安全地创建一个由智能指针 `sp` 管理的 `ProcessState` 对象
//相当于new ProcessState(driver)
gProcess = sp<ProcessState>::make(driver);
});
...
return gProcess;
}
//ProcessState构造函数
ProcessState::ProcessState(const char *driver)
//`String8` 是 Android 的字符串类(UTF-8 编码)
//将 C 字符串转换为 `String8` 对象
: mDriverName(String8(driver))
//打开 Binder 驱动
, mDriverFD(open_driver(driver))
//内存映射地址初始化,MAP_FAILED标准 C 库定义的内存映射失败返回值
//存储 `mmap()` 返回的虚拟地址起始位置,用于 Binder 事务的数据传输缓冲区
, mVMStart(MAP_FAILED)
//保护线程计数变量的互斥锁
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
//当前正在执行 Binder 调用的线程数
, mExecutingThreadsCount(0)
//等待退出或等待线程数减少的线程数
, mWaitingForThreads(0)
//最大线程数,15
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
...
{
if (mDriverFD >= 0) {
//内存映射
//`BINDER_VM_SIZE` 映射大小
//PROT_READ只读映射,`MAP_PRIVATE | MAP_NORESERVE` | 私有映射,不预留交换空间
//`mDriverFD` | Binder 设备文件描述符
//`offset` | `0` | 文件偏移量
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
//内存映射失败
if (mVMStart == MAP_FAILED) {
//关闭文件描述符
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
#ifdef __ANDROID__
//mDriverFD < 0 终止进程
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver);
#endif
}
- 核心工作 :程序会创建一个
BootAnimation对象,这个对象继承自Thread类,会开启一个新线程来执行动画播放。
scss
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
: Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
//创建 SurfaceComposerClient
//与 SurfaceFlinger 服务通信的客户端
//用于创建和管理显示 Surface
mSession = new SurfaceComposerClient();
//判断是否是关机动画
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
if (powerCtl.empty()) {
mShuttingDown = false;
} else {
mShuttingDown = true;
}
}
//当智能指针 `sp<T>` 第一次引用对象时,系统会自动调用
void BootAnimation::onFirstRef() {
//注册 SurfaceFlinger 死亡监听
//SurfaceFlinger 是核心图形服务,如果它崩溃了,动画无法继续
//监听死亡事件可以在 SurfaceFlinger 重启后**重新初始化**或**优雅退出**
status_t err = mSession->linkToComposerDeath(this);
//检查注册是否成功
if (err == NO_ERROR) {
//预加载动画
preloadAnimation();
}
}
// 线程循环
//- 返回值控制循环是否继续:
//- `true`:继续循环(再次调用 `threadLoop()`)
//- `false`:退出循环,线程结束
bool BootAnimation::threadLoop() {
bool result;
//动画 ZIP 文件为空
if (mZipFileName.isEmpty()) {
//播放内置的 Android 字样动画 | 无 ZIP 文件,使用 OpenGL 直接绘制
result = android();
} else {
//播放从 ZIP 加载的自定义动画 | 解析 desc.txt,逐帧显示图片
result = movie();
}
//通知音频回调对象释放资源,停止播放音效,释放 `AudioTrack` 等资源
mCallbacks->shutdown();
//解除 EGL 上下文绑定
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
//销毁 EGL 资源,释放 GPU 驱动资源
eglDestroyContext(mDisplay, mContext);
//销毁 EGL Surface(渲染目标),释放帧缓冲区
eglDestroySurface(mDisplay, mSurface);
//释放智能指针引用
mFlingerSurface.clear();
mFlingerSurfaceControl.clear();
//终止 EGL 显示连接,释放所有 EGL 资源(上下文、表面等),关闭与 GPU 驱动的连接
eglTerminate(mDisplay);
//释放当前线程的 EGL 状态,清理线程局部存储(TLS),避免线程退出时资源泄漏
eglReleaseThread();
//通知 Binder 驱动停止处理该进程的事务,释放 Binder 引用和内存映射
//准备进程退出
IPCThreadState::self()->stopProcess();
return result;
}
BootAnimation::movie() 是 Android 开机动画自定义动画播放的核心函数,负责初始化 OpenGL 环境、加载字体、播放动画和清理资源。
scss
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
bool BootAnimation::movie() {
if (mAnimation == nullptr) {
//尝试重新加载
mAnimation = loadAnimation(mZipFileName);
}
//加载失败
if (mAnimation == nullptr)
return false;
//音频回调初始化
//有些 Part 可能引用嵌套动画(`$SYSTEM`),需要先初始化嵌套动画的音频
//再初始化当前动画的音频
for (const Animation::Part& part : mAnimation->parts) {
if (part.animation != nullptr) {
mCallbacks->init(part.animation->parts);
}
}
mCallbacks->init(mAnimation->parts);
...
//播放动画
playAnimation(*mAnimation);
...
//释放动画对象
releaseAnimation(mAnimation);
mAnimation = nullptr;
//`false`:线程退出
return false;
}
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
//已执行的淡入淡出帧数
int fadedFramesCount = 0;
//上次显示的进度值(0-100)
int lastDisplayedProgress = 0;
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);
// 如果 Part 包含嵌套动画,递归调用 `playAnimation()`
if (part.animation != nullptr) {
playAnimation(*part.animation);
//如果收到退出信号,停止播放
if (exitPending())
break;
continue; //to next part
}
//未达到播放次数,正在执行淡出效果
for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
//检查是否需要提前停止
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
//通知音频回调播放 Part 的音效,每个 Part 播放一次
mCallbacks->playPart(i, part, r);
//设置背景色
glClearColor(
part.backgroundColor[0],
part.backgroundColor[1],
part.backgroundColor[2],
1.0f);
// 进度显示
int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
//动画启用了进度显示,是最后一个 Part,当前进度不为 0
bool displayProgress = animation.progressEnabled &&
(i == (pcount -1)) && currentProgress != 0;
for (size_t j=0 ; j<fcount ; j++) {
//检查是否需要提前停止,按顺序播放每一帧
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
//处理显示事件
processDisplayEvents();
//计算居中位置
const int animationX = (mWidth - animation.width) / 2;
const int animationY = (mHeight - animation.height) / 2;
//获取帧和时间
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
//纹理初始化
...
//渲染帧
const int xc = animationX + frame.trimX;
const int yc = animationY + frame.trimY;
Region clearReg(Rect(mWidth, mHeight));
...
//淡入淡出效果
if (exitPending() && part.hasFadingPhase()) {
fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
++fadedFramesCount);
...
}
//绘制时钟,
//时钟显示条件:时钟功能启用, 时间准确,Part 配置了时钟位置
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
//绘制进度
if (displayProgress) {
int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
if (lastDisplayedProgress != 100) {
//延迟 100ms
usleep(100000);
if (lastDisplayedProgress < newProgress) {
//每次只增加 1,防止进度跳跃
lastDisplayedProgress++;
}
}
...
drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
}
handleViewport(frameDuration);
//交换缓冲区
//将后台缓冲区交换到前台,显示渲染的图像,等待垂直同步(VSync)
eglSwapBuffers(mDisplay, mSurface);
//帧率控制
nsecs_t now = systemTime();
//帧时长 = 1秒 / FPS
//实际耗时 = 当前时间 - 上一帧时间
//延迟 = 帧时长 - 实际耗时
nsecs_t delay = frameDuration - (now - lastFrame);
lastFrame = now;
//如果延迟 > 0
if (delay > 0) {
struct timespec spec;
spec.tv_sec = (now + delay) / 1000000000;
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
//睡眠延迟时间
//高精度睡眠,使用单调时钟,不受系统时间调整影响
//支持绝对时间,更准确
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
} while (err<0 && errno == EINTR);
}
//检查动画是否退出
checkExit();
}
...
//Part 停止逻辑
//收到退出信号,无限循环 Part (`count == 0`)
//达到目标插入值,没有淡出阶段
if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
!part.hasFadingPhase()) {
if (lastDisplayedProgress != 0 && lastDisplayedProgress != 100) {
android::base::SetProperty(PROGRESS_PROP_NAME, "100");
continue;
}
break;
}
}
}
...
return true;
}
- 资源加载与解析 :
BootAnimation会查找并加载动画资源包(通常是/system/media/bootanimation.zip),然后解析其内部的desc.txt描述文件,获取动画的帧率、分辨率、图片组等信息。
c
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
bool BootAnimation::preloadAnimation() {
//查找动画 ZIP 文件的路径
findBootAnimationFile();
if (!mZipFileName.isEmpty()) {
//加载并解析动画描述文件
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}
return false;
}
//查找并加载动画资源包
void BootAnimation::findBootAnimationFile() {
char decrypt[PROPERTY_VALUE_MAX];
//读取加密状态
//vold.decrypt由 `vold`(Volume Daemon,卷守护进程)管理,指示设备加密状态和解密进度
property_get("vold.decrypt", decrypt, "");
//判断是否使用加密动画,**`atoi()` 函数**:将字符串转换为整数
bool encryptedAnimation = atoi(decrypt) != 0 ||
!strcmp("trigger_restart_min_framework", decrypt);
//正常开机,设备处于加密状态
if (!mShuttingDown && encryptedAnimation) {
//PRODUCT加密动画路径"/product/media/bootanimation-encrypted.zip";
//SYSTEM加密动画路径 "/system/media/bootanimation-encrypted.zip";
//`/product/media/bootanimation-encrypted.zip`(产品定制)
//`/system/media/bootanimation-encrypted.zip`(系统默认)
static const std::vector<std::string> encryptedBootFiles = {
PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE,
};
//找到文件,`mZipFileName` 已设置
if (findBootAnimationFileInternal(encryptedBootFiles)) {
return;
}
}
//读取深色主题配置,0=浅色,1=深色
const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
//开机动画文件列表
//APEX 模块(Android 10+ 新特性),Product 分区(深色或浅色)
//OEM 厂商定制,system 系统默认
//"/apex/com.android.bootanimation/etc/bootanimation.zip";
//"/product/media/bootanimation-dark.zip"
//"/product/media/bootanimation.zip";
//"/oem/media/bootanimation.zip";
//"/system/media/bootanimation.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
};
//关机动画文件列表
//"/product/media/shutdownanimation.zip";
//"/oem/media/shutdownanimation.zip";
//"/system/media/shutdownanimation.zip";
static const std::vector<std::string> shutdownFiles = {
PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""
};
//用户空间重启动画文件列表
//用户空间重启,Android 11 引入的新特性,在不重启内核的情况下
//重启用户空间(framework、服务等)比完整重启更快(~5-10秒 vs ~20-30秒)
//提示"正在重新启动..."
//"/product/media/userspace-reboot.zip";
//"/oem/media/userspace-reboot.zip";
//"/system/media/userspace-reboot.zip";
static const std::vector<std::string> userspaceRebootFiles = {
PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE, OEM_USERSPACE_REBOOT_ANIMATION_FILE,
SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE,
};
//选择动画类型
if (android::base::GetBoolProperty("sys.init.userspace_reboot.in_progress", false)) {
findBootAnimationFileInternal(userspaceRebootFiles);
} else if (mShuttingDown) {
findBootAnimationFileInternal(shutdownFiles);
} else {
findBootAnimationFileInternal(bootFiles);
}
}
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
//SortedVector<String8> mLoadedFiles; // 已加载文件列表
//未找到
if (mLoadedFiles.indexOf(fn) >= 0) {
return nullptr;
}
//打开 ZIP 文件
ZipFileRO *zip = ZipFileRO::open(fn);
if (zip == nullptr) {
return nullptr;
}
//创建 Animation 对象
Animation *animation = new Animation;
animation->fileName = fn;
animation->zip = zip;
animation->clockFont.map = nullptr;
//将文件名加入已加载列表
mLoadedFiles.add(animation->fileName);
//解析动画文件
parseAnimationDesc(*animation);
//预加载 ZIP 资源
if (!preloadZip(*animation)) {
releaseAnimation(animation);
return nullptr;
}
//从已加载列表中移除文件名
//允许该文件在后续再次加载(如果需要)
mLoadedFiles.remove(fn);
return animation;
}
BootAnimation::parseAnimationDesc() 是 Android 开机动画文件解析的核心函数 ,负责将 desc.txt 转换为内存中的动画数据结构。
ini
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
bool BootAnimation::parseAnimationDesc(Animation& animation) {
String8 desString;
//读取 desc.txt
if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
//获取字符串指针
char const* s = desString.string();
// 无限循环
for (;;) {
//查找子串 `"\n"` 的位置,返回指向换行符的指针
const char* endl = strstr(s, "\n");
if (endl == nullptr) break;
//构造字符串,长度为 `endl - s`(不包括换行符)
String8 line(s, endl - s);
const char* l = line.string();
int fps = 0;
int width = 0;
int height = 0;
int count = 0;
int pause = 0;
int progress = 0;
//渐变帧数(淡入淡出效果)
int framesToFadeCount = 0;
char path[ANIM_ENTRY_NAME_MAX];
//背景颜色(RGB 十六进制)
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
char pathType;
//读取位置记录
int nextReadPos;
//解析
int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress);
if (topLineNumbers == 3 || topLineNumbers == 4) {
animation.width = width;
animation.height = height;
animation.fps = fps;
//progressEnabled `1`:显示进度条,`0`:不显示进度条
if (topLineNumbers == 4) {
animation.progressEnabled = (progress != 0);
} else {
animation.progressEnabled = false;
}
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
if (pathType == 'f') {
sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1,
clockPos2);
} else {
sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2);
}
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.framesToFadeCount = framesToFadeCount;
part.count = count;
part.pause = pause;
part.path = path;
part.audioData = nullptr;
part.animation = nullptr;
if (!parseColor(color, part.backgroundColor)) {
SLOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
part.backgroundColor[1] = 0.0f;
part.backgroundColor[2] = 0.0f;
}
parsePosition(clockPos1, clockPos2, &part.clockPosX, &part.clockPosY);
animation.parts.add(part);
}
else if (strcmp(l, "$SYSTEM") == 0) {
//引用系统默认动画
Animation::Part part;
part.playUntilComplete = false;
part.framesToFadeCount = 0;
part.count = 1;
part.pause = 0;
part.audioData = nullptr;
part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
if (part.animation != nullptr)
animation.parts.add(part);
}
s = ++endl;
}
return true;
}
BootAnimation::preloadZip() 是 Android 开机动画ZIP 文件资源预加载的核心函数,负责将所有图片、音频、字体等资源从 ZIP 中加载到内存。
ini
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
bool BootAnimation::preloadZip(Animation& animation) {
const size_t pcount = animation.parts.size();
void *cookie = nullptr;
ZipFileRO* zip = animation.zip;
//初始化 ZIP 文件内容遍历
//创建一个迭代器上下文(`cookie`),以便后续通过 `nextEntry()`
//按顺序访问 ZIP 包中的每一个文件条目。
if (!zip->startIteration(&cookie)) {
return false;
}
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
//遍历 ZIP 条目
while ((entry = zip->nextEntry(cookie)) != nullptr) {
//获取文件名
const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
continue;
}
//解析路径和文件名
const String8 entryName(name);
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
if (entryName == CLOCK_FONT_ZIP_NAME) {
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
animation.clockFont.map = map;
}
continue;
}
if (entryName == PROGRESS_FONT_ZIP_NAME) {
///将文件内容映射到内存
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
animation.progressFont.map = map;
}
continue;
}
//加载 Part 资源
//遍历所有 Part,检查文件路径是否匹配 Part 的路径
for (size_t j = 0; j < pcount; j++) {
if (path == animation.parts[j].path) {
uint16_t method;
// supports only stored png files
if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
//未压缩(直接存储)
if (method == ZipFileRO::kCompressStored) { //只支持未压缩文件
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
Animation::Part& part(animation.parts.editItemAt(j));
//加载音频文件
//必须是 WAV 格式(PCM)
//文件名固定为 `audio.wav`
//一个 Part 最多一个音频文件
if (leaf == "audio.wav") {
//**`audioData` 指针**:
// 直接指向内存映射区域
//无需复制数据
//播放时直接读取
part.audioData = (uint8_t *)map->getDataPtr();
part.audioLength = map->getDataLength();
} else if (leaf == "trim.txt") {
//加载裁剪数据
//指定每帧图片的裁剪区域
//格式:`widthxheight+x+y`
//用于优化图片显示
part.trimData.setTo((char const*)map->getDataPtr(),
map->getDataLength());
} else {
//加载图片帧
Animation::Frame frame;
frame.name = leaf;
frame.map = map;
frame.trimWidth = animation.width;
frame.trimHeight = animation.height;
frame.trimX = 0;
frame.trimY = 0;
part.frames.add(frame);
}
}
} else {
}
}
}
}
}
}
//解析 trim.txt
for (Animation::Part& part : animation.parts) {
const char* trimDataStr = part.trimData.string();
...
}
}
//结束迭代
zip->endIteration(cookie);
return true;
}
结束退出:响应系统就绪信号
- 桌面(Launcher)就绪,发出"空闲"信号 :当桌面的根Activity完成绘制并处于空闲状态时,它的主线程会通过
MessageQueue.IdleHandler回调机制,向ActivityManagerService(AMS) 发送一个activityIdle通知。 - AMS 判断"时机已到" :AMS 收到这个通知后,会将其视为"系统已准备好启动"的关键信号,然后调用
enableScreenAfterBoot()方法。
scss
//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mH.post(() -> {
if (finishBooting) {
//AMS
mAmInternal.finishBooting();
}
if (enableScreen) {
//ATMS
ActivityTaskManagerInternal
mInternal.enableScreenAfterBoot(isBooted());
}
});
}
public void enableScreenAfterBoot(boolean booted) {
...
mWindowManager.enableScreenAfterBoot();
...
}
- WMS 最终决策并发出指令 :
enableScreenAfterBoot()会调用到WindowManagerService(WMS) 的performEnableScreen()方法。 在performEnableScreen()中,WMS会通过Binder跨进程通信 ,向SurfaceFlinger服务发送一个名为BOOT_FINISHED的消息。
csharp
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void enableScreenAfterBoot() {
synchronized (mGlobalLock) {
if (mSystemBooted) {
return;
}
mSystemBooted = true;
//移除所有 BOOT_MSG 相关的消息
hideBootMessagesLocked();
//启动超时监控
mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
}
...
performEnableScreen();
}
private void performEnableScreen() {
synchronized (mGlobalLock) {
...
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
//发送 Binder 消息到 SurfaceFlinger
//消息代码 = `BOOT_FINISHED`
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION,
data, null, 0);
data.recycle();
}
} catch (RemoteException ex) {
ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
}
...
}
...
}
//frameworks/native/libs/gui/include/gui/ISurfaceComposer.h
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
enum ISurfaceComposerTag {
//WMS 发消息的时候发的是IBinder.FIRST_CALL_TRANSACTION
//在SurfaceFlinger中转化成了BOOT_FINISHED
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
...
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
};
//Binder 客户端代理 (BpSurfaceComposer)
//frameworks/native/libs/gui/ISurfaceComposer.cpp
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
...
void bootFinished() override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
...
}
- SurfaceFlinger 设置退出属性 :SurfaceFlinger收到
BOOT_FINISHED消息后,会执行内部的bootFinished()函数。这个函数的最终动作,就是通过property_set将系统属性service.bootanim.exit的值设置为"1"。
arduino
//Binder 服务端 (BnSurfaceComposer)
//frameworks/native/libs/gui/ISurfaceComposer.cpp
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
...
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
return NO_ERROR;
}
...
}
}
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::bootFinished() {
if (mBootFinished == true) {
return;
}
mBootFinished = true;
...
//设置退出动画属性
property_set("service.bootanim.exit", "1");
...
}
- 动画退出 :开机动画进程
bootanimation内部有一个每帧都会执行的checkExit()函数。它持续检查service.bootanim.exit属性,一旦发现值变为"1",便会调用requestExit(),从而安全、优雅地结束动画进程。
scss
//frameworks/base/cmds/bootanimation/BootAnimation.cpp
void BootAnimation::checkExit() {
char value[PROPERTY_VALUE_MAX];
//EXIT_PROP_NAME[] = "service.bootanim.exit";
property_get(EXIT_PROP_NAME, value, "0");
//`atoi` 函数是 C 标准库 `<stdlib.h>` 中定义的字符串转整数函数
//检测service.bootanim.exit 是否等于1
int exitnow = atoi(value);
if (exitnow) {
//请求退出
requestExit();
}
}
requestExit() 方法定义在 Android 系统的 Thread 基类 中,位于 system/core/libutils/include/utils/Thread.h 头文件里。它的作用是"请求"退出,而非"强制"终止 :调用后,它会设置一个内部的退出标志 (mExitPending = true),但不会立即停止线程。