上一篇我们完整梳理了 Launcher3 桌面启动全流程,理解系统如何拉起桌面应用并渲染 UI。 但有一个直观问题容易被忽略:从按下电源键到 Launcher 完整显示,中间长达数秒的时间屏幕并非黑屏,而是持续播放厂商定制开机动画,完整链路为: Google Logo 静态帧 → BootAnimation 逐帧动画 → Launcher 桌面
本篇围绕 4 个核心问题,结合完整 Native+Java 层源码拆解全链路:
- 开机动画进程由谁启动?
- 为何 Framework 服务未完全就绪时,动画就能正常渲染输出?
- SurfaceFlinger 与 BootAnimation 的依赖、协作关系是什么?
- BootAnimation 如何精准感知退出时机,动画结束后 Launcher 如何接管屏幕?
完整系统启动骨架链路: init 进程 → SurfaceFlinger → BootAnimation → WindowManagerService → Launcher
一、BootAnimation 进程启动源头:init 配置与启动规则
1.1 启动配置文件 bootanim.rc
路径:frameworks/base/cmds/bootanimation/bootanim.rc
bash
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
ioprio rt 0
task_profiles MaxPerformance
关键配置释义:
disabled:默认不会由 init 自动拉起,必须由 SurfaceFlinger 初始化完成后通过属性ctl.start bootanim主动触发启动;class core animation:归属核心动画进程组,系统关机时统一回收;user/group graphics:赋予图形、音频硬件访问权限,保证 OpenGL 渲染、动画音效可用;oneshot:进程执行完成后自动销毁,不会反复重启;ioprio rt 0:实时 IO 优先级,避免动画加载、解码卡顿。
1.2 启动时序关键:SurfaceFlinger 是前置依赖
内核启动后第一个用户态进程 init,会优先解析 rc 脚本启动 surfaceflinger 图形合成服务。 SurfaceFlinger 完成 EGL、显示硬件、BufferQueue 初始化后,会通过属性线程执行: property_set("ctl.start", "bootanim") 通知 init 拉起 /system/bin/bootanimation 二进制程序。 依赖逻辑:BootAnimation 所有渲染、图层创建都依赖 SurfaceFlinger,因此必须等 SF 就绪后才能启动动画进程。
1.3 程序入口 bootanimation_main.cpp
路径:frameworks/base/cmds/bootanimation/bootanimation_main.cpp
cpp
int main()
{
// 提升进程调度优先级为显示级,保障动画流畅
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
// 读取系统属性,判断是否禁用开机动画
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
// 初始化Binder线程池,后续与SF、WMS通信依赖Binder
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// 实例化开机动画核心类,加载/system/media/bootanimation.zip,初始化音频回调
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
// 阻塞等待SurfaceFlinger服务Binder通道可用
waitForSurfaceFlinger();
// 启动动画子线程,主线程阻塞等待播放完成
boot->run("BootAnimation", PRIORITY_DISPLAY);
ALOGV("Boot animation set up. Joining binder thread pool.");
// 动画播放结束,主线程进入Binder循环,维持进程不立即销毁
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
辅助判断函数 bootAnimationDisabled ()
cpp
bool bootAnimationDisabled() {
char value[PROPERTY_VALUE_MAX];
// 调试属性:强制关闭动画
property_get("debug.sf.nobootanimation", value, "0");
if (atoi(value) > 0) {
return true;
}
// 静默开机属性判断
property_get("ro.boot.quiescent", value, "0");
if (atoi(value) > 0) {
// 静默开机仅在对应开关开启时才显示动画
if (!property_get_bool("ro.bootanim.quiescent.enabled", false)) {
return true;
}
}
return false;
}
二、BootAnimation 线程执行时序:readyToRun → threadLoop 完整流程
BootAnimation 继承系统 Thread 基类,boot->run() 内部会创建独立 pthread 子线程,底层静态入口 _threadLoop 执行固定两步逻辑:
- 优先执行
readyToRun():一次性初始化 EGL、多屏幕 Surface、着色器、渲染环境; - 初始化无异常才进入
do-while循环,反复执行threadLoop()播放动画。
完整时序图
bash
boot->run("BootAnimation", PRIORITY_DISPLAY);
Thread::run() → pthread_create 创建独立动画子线程
底层静态入口 _threadLoop()
第一步:BootAnimation::readyToRun()
mAssets.addDefaultAssets();
initDisplaysAndSurfaces() // 初始化多屏幕、EGL、图层Surface
返回 NO_ERROR 代表图形环境就绪
第二步:进入 do-while 循环,循环调用 threadLoop()
threadLoop()
movie() // 加载zip动画资源
playAnimation() // 逐帧渲染主逻辑
每帧渲染末尾调用 checkExit() 检测退出属性
2.1 readyToRun ():动画播放前一次性图形环境初始化
路径:frameworks/base/cmds/bootanimation/BootAnimation.cpp
cpp
status_t BootAnimation::readyToRun() {
ATRACE_CALL();
// 注册系统资源加载路径,用于读取zip内图片、字体
mAssets.addDefaultAssets();
// 核心:初始化屏幕、EGL渲染上下文、Surface图层
return initDisplaysAndSurfaces();
}
核心初始化 initDisplaysAndSurfaces ()
cpp
status_t BootAnimation::initDisplaysAndSurfaces() {
// 1. 通过SurfaceComposerClient获取本机所有物理屏幕ID
std::vector<PhysicalDisplayId> displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
if (displayIds.empty()) {
SLOGE("Failed to get ID for any displays");
return NAME_NOT_FOUND;
}
// 多开机动画开关关闭时,只保留主屏,过滤所有外接副屏
if (!com::android::graphics::bootanimation::flags::multidisplay()) {
displayIds.erase(displayIds.begin() + 1, displayIds.end());
}
// 遍历所有有效屏幕,获取屏幕唯一Token保存
for (const auto id : displayIds) {
const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id);
if (!token.isValid()) {
SLOGE("Failed to get display token for display %" PRIu64, id.value);
return NAME_NOT_FOUND;
}
mDisplays.push_back({.displayToken = token});
}
// 全局EGL初始化,创建GLES2.0渲染上下文(所有屏幕共享一套GL上下文)
mEgl = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEgl, nullptr, nullptr);
EGLConfig config = getEglConfig(mEgl);
EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
mEglContext = eglCreateContext(mEgl, config, nullptr, contextAttributes);
// 读取系统属性限制图层最大分辨率,防止超大折叠屏显存溢出
mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
// 为每一块物理屏幕创建独立Surface图层与EGL渲染窗口
for (size_t displayIdx = 0; displayIdx < mDisplays.size(); displayIdx++) {
auto& display = mDisplays[displayIdx];
DisplayMode displayMode;
// 查询屏幕当前生效分辨率、刷新率
status_t error = SurfaceComposerClient::getActiveDisplayMode(display.displayToken, &displayMode);
if (error != NO_ERROR) return error;
ui::Size resolution = displayMode.resolution;
// 根据全局限制裁剪分辨率
resolution = limitSurfaceSize(resolution.width, resolution.height);
// 向SurfaceFlinger创建专属图层BootAnimation,像素格式RGB565不透明,节省显存
display.surfaceControl = session()->createSurface(
String8("BootAnimation"), resolution.width, resolution.height,
PIXEL_FORMAT_RGB_565, ISurfaceComposerClient::eOpaque);
// 将图层绑定到对应屏幕的LayerStack,输出画面至当前物理屏
configureDisplayAndLayerStack(display, ui::LayerStack::fromValue(displayIdx));
display.surface = display.surfaceControl->getSurface();
// 基于Native窗口创建EGL渲染表面,作为OpenGL绘制目标
display.eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr);
// 查询EGL表面实际宽高
EGLint w, h;
eglQuerySurface(mEgl, display.eglSurface, EGL_WIDTH, &w);
eglQuerySurface(mEgl, display.eglSurface, EGL_HEIGHT, &h);
// 绑定当前屏幕EGL上下文,后续GL绘制全部输出到此屏幕
if (eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, mEglContext) == EGL_FALSE) {
return NO_INIT;
}
display.initWidth = display.width = w;
display.initHeight = display.height = h;
mTargetInset = -1;
// 根据系统属性 ro.bootanim.set_orientation_<display_id> 旋转动画画面,适配平板横屏开机
rotateAwayFromNaturalOrientationIfNeeded(display);
// 初始化投影矩阵,用于动画居中、缩放、旋转变换
projectSceneToWindow(display);
}
// 注册屏幕热插拔、分辨率变化监听,动态适配画面
mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
status_t status = mDisplayEventReceiver->initCheck();
SLOGE_IF(status != NO_ERROR, "DisplayEventReceiver init failed: %d", status);
// 将事件fd注册到主线程Looper,异步接收屏幕变更
mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
new DisplayEventCallback(this), nullptr);
return NO_ERROR;
}
本段核心价值
Framework(system_server、WMS、AMS)尚未启动时,仅依赖 SurfaceFlinger 即可完成图层、EGL 初始化,这也是开机动画能提前渲染的核心原因: BootAnimation 与 SF 均为 Native 独立进程,不依赖 Java 层系统服务。
2.2 threadLoop ():动画主循环入口
cpp
bool BootAnimation::threadLoop() {
ATRACE_CALL();
bool result;
// 预编译图片渲染着色器
initShaders();
// 判断是否存在bootanimation.zip资源
if (mZipFileName.empty()) {
ALOGD("No animation zip file, render default Android logo");
// 无自定义动画,渲染原生静态Android文字Logo
result = android(mDisplays.front());
} else {
// 标准流程:加载并播放zip逐帧动画
result = movie();
}
// 动画播放完毕,统一销毁全部图形资源
mCallbacks->shutdown();
// 解绑EGL上下文
eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mEgl, mEglContext);
// 释放所有屏幕EGL窗口与Surface图层
for (auto& display : mDisplays) {
eglDestroySurface(mEgl, display.eglSurface);
display.surface.clear();
display.surfaceControl.clear();
}
eglTerminate(mEgl);
eglReleaseThread();
// 终止Binder通信
IPCThreadState::self()->stopProcess();
return result;
}
2.3 movie ():解析 zip 动画资源,配置渲染参数
cpp
bool BootAnimation::movie() {
ATRACE_CALL();
// 解析/system/media/bootanimation.zip,加载desc.txt分段配置、所有帧图片
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
if (mAnimation == nullptr)
return false;
// 递归初始化各分段音频播放回调,处理嵌套子动画
for (const Animation::Part& part : mAnimation->parts) {
if (part.animation != nullptr) {
mCallbacks->init(part.animation->parts);
}
}
mCallbacks->init(mAnimation->parts);
// 检测动画分段是否配置右上角时钟,初始化时钟渲染参数
bool anyPartHasClock = false;
for (size_t i = 0; i < mAnimation->parts.size(); i++) {
if (validClock(mAnimation->parts[i])) {
anyPartHasClock = true;
break;
}
}
if (!anyPartHasClock) {
mClockEnabled = false;
} else {
// 读取开关属性,控制时钟是否显示
mClockEnabled = android::base::GetBoolProperty(CLOCK_ENABLED_PROP, false);
}
// 检测GPU是否支持非2次幂纹理,适配各类图片素材
mUseNpotTextures = false;
String8 glExtensions((const char*)glGetString(GL_EXTENSIONS));
if (glExtensions.contains("GL_OES_texture_npot")) {
mUseNpotTextures = true;
}
// 配置全局OpenGL混合、抗锯齿基础参数
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载时钟、开机进度条字体纹理
bool clockFontOk = false;
if (mClockEnabled) {
clockFontOk = (initFont(&mClockFont, "clockfont") == NO_ERROR);
if (!clockFontOk) mClockEnabled = false;
}
initFont(&mProgressFont, "progressfont");
// 时钟校准子线程,实时更新系统时间用于渲染
if (mClockEnabled && !mTimeIsAccurate) {
mTimeCheckThread = new TimeCheckThread(this);
mTimeCheckThread->run("BootAnimTime", PRIORITY_LOW);
}
// 初始化动态主题渐变着色器
if (mAnimation->dynamicColoringEnabled) {
initDynamicColorShaders();
}
// 核心:阻塞播放完整动画,逐帧渲染逻辑
playAnimation(*mAnimation);
// 动画播放完成,回收时钟校准子线程
if (mTimeCheckThread != nullptr) {
mTimeCheckThread->requestExit();
mTimeCheckThread->join();
mTimeCheckThread = nullptr;
}
// 释放时钟纹理显存
if (clockFontOk) {
glDeleteTextures(1, &mClockFont.textureId);
}
glDeleteTextures(1, &mProgressFont.textureId);
// 释放全部动画帧资源
releaseAnimation(mAnimation);
mAnimation = nullptr;
return false;
}
2.4 playAnimation ():逐帧渲染核心逻辑(最关键循环)
cpp
bool BootAnimation::playAnimation(const Animation& animation) {
ATRACE_CALL();
const size_t totalParts = animation.parts.size();
// 计算单帧标准间隔(纳秒),锁定动画FPS
nsecs_t frameIntervalNs = s2ns(1) / animation.fps;
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms",
mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
int fadeFrameCount = 0; // 关机淡出帧计数
int lastProgress = 0; // 开机进度条缓存值
int colorTransStart = animation.colorTransitionStart;
int colorTransEnd = animation.colorTransitionEnd;
// 第一层循环:遍历desc.txt定义的所有动画分段Part
for (size_t partIdx = 0; partIdx < totalParts; partIdx++) {
const Animation::Part& part = animation.parts[partIdx];
const size_t frameCountInPart = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
// 支持嵌套子zip动画,递归播放
if (part.animation != nullptr) {
playAnimation(*part.animation);
if (exitPending()) break;
continue;
}
// 第二层循环:控制分段循环次数(无限循环/指定次数/淡出收尾)
for (int loopIdx = 0;
part.loopCount == 0 || loopIdx < part.loopCount || fadeFrameCount > 0;
loopIdx++) {
// 检测是否需要终止当前分段
if (shouldStopPart(part, fadeFrameCount, lastProgress)) break;
// 动态主题属性延迟加载重试逻辑
if (animation.dynamicColoringEnabled && !mDynamicColorLoaded
&& (part.useDynamicColor || part.postDynamicColor)) {
SLOGD("Reload dynamic color system props");
loadDynamicColors();
if (mDynamicColorLoaded) {
int transLen = colorTransEnd - colorTransStart;
if (part.postDynamicColor) {
colorTransStart = 0;
colorTransEnd = std::min(transLen, (int)frameCountInPart - 1);
}
}
}
// 触发当前分段配套音频播放
mCallbacks->playPart(partIdx, part, loopIdx);
// 设置分段背景清屏色
glClearColor(part.bgColor[0], part.bgColor[1], part.bgColor[2], 1.0f);
bool drawProgressBar = animation.progressEnabled
&& partIdx == totalParts - 1
&& android::base::GetIntProperty(PROGRESS_PROP, 0) != 0;
// 第三层循环:遍历当前分段内所有图片帧
for (size_t frameIdx = 0; frameIdx < frameCountInPart; frameIdx++) {
if (shouldStopPart(part, fadeFrameCount, lastProgress)) break;
// 计算动态主题渐变系数 [0 ~ 1]
float colorProgress = part.useDynamicColor
? std::clamp(
((float)frameIdx - colorTransStart) / std::max(colorTransEnd - colorTransStart, 1.0f),
0.0f, 1.0f)
: (part.postDynamicColor ? 1.0f : 0.0f);
// 处理屏幕分辨率、热插拔事件
processDisplayEvents();
const Animation::Frame& frame = part.frames[frameIdx];
nsecs_t frameBeginTime = systemTime();
// 纹理复用逻辑:首次循环创建纹理,后续直接复用显存,避免重复解码
if (loopIdx > 0) {
glBindTexture(GL_TEXTURE_2D, frame.textureId);
} else {
if (part.loopCount != 1) {
glGenTextures(1, &frame.textureId);
glBindTexture(GL_TEXTURE_2D, frame.textureId);
}
int w, h;
// 解码图片至纹理,保留原始Alpha通道不预乘
loadTexture(frame.buffer, &w, &h, false);
}
// 收到退出信号且分段配置淡出动画,计算透明度衰减值
float fadeAlpha = 0.0f;
if (exitPending() && part.enableFadeOut) {
fadeAlpha = (float)(++fadeFrameCount) / part.fadeTotalFrames;
if (fadeFrameCount >= part.fadeTotalFrames) {
fadeFrameCount = MAX_FADE_FRAME;
}
}
// 多屏幕循环:逐屏绘制当前帧
for (const auto& display : mDisplays) {
// 切换EGL渲染目标至当前屏幕
eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, mEglContext);
// 计算画面缩放比例,实现居中适配屏幕
float scaleW = (float)display.width / display.originalWidth;
float scaleH = (float)display.height / display.originalHeight;
int animBaseX = (display.width - animation.width * scaleW) / 2;
int animBaseY = (display.height - animation.height * scaleH) / 2;
// 帧裁剪坐标缩放换算
int cropW = frame.cropW * scaleW;
int cropH = frame.cropH * scaleH;
int cropX = frame.cropX * scaleW;
int cropY = frame.cropY * scaleH;
int drawX = animBaseX + cropX;
// OpenGL Y轴倒置,修正绘制纵坐标
int drawY = display.height - (animBaseY + cropY + cropH);
// 初始化正交投影矩阵
projectSceneToWindow(display);
glClear(GL_COLOR_BUFFER_BIT);
// 绑定图片着色器,传入渐变、淡出透明度参数
glUseProgram(mImageShaderProgram);
glUniform1i(mTexLoc, 0);
glUniform1f(mFadeLoc, fadeAlpha);
if (animation.dynamicColoringEnabled) {
glUniform1f(mColorProgressLoc, colorProgress);
}
glEnable(GL_BLEND);
// 绘制带纹理矩形(核心图片渲染)
drawTexturedRect(drawX, drawY, cropW, cropH, display);
glDisable(GL_BLEND);
// 叠加绘制右上角时钟文字
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClockText(mClockFont, part.clockX, part.clockY, display);
}
// 叠加绘制底部开机进度百分比
if (drawProgressBar) {
int newProgress = android::base::GetIntProperty(PROGRESS_PROP, 0);
if (lastProgress != 100) {
usleep(100000); // 放慢进度条刷新速度,避免跳动过快
if (lastProgress < newProgress) lastProgress++;
}
drawProgressText(lastProgress, mProgressFont, TEXT_CENTER, animation.height / 3, display);
}
// 交换前后双缓冲,画面输出到物理屏幕
eglSwapBuffers(mEgl, display.eglSurface);
}
// 帧间隔休眠,锁定FPS,防止动画加速播放
nsecs_t now = systemTime();
nsecs_t sleepNs = frameIntervalNs - (now - frameBeginTime);
if (sleepNs > 0) {
struct timespec sleepSpec;
sleepSpec.tv_sec = (now + sleepNs) / 1000000000;
sleepSpec.tv_nsec = (now + sleepNs) % 1000000000;
int ret;
// 绝对单调时钟休眠,被信号中断自动重试
do {
ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepSpec, nullptr);
} while (ret == EINTR);
}
// 每帧渲染完成,检测是否收到退出指令(核心退出检测入口)
checkExit();
}
// 当前分段所有帧播放完毕,执行分段暂停pause
int pauseUs = part.pauseFrameCount * ns2us(frameIntervalNs);
while (pauseUs > 0 && !exitPending()) {
if (pauseUs > MAX_CHECK_INTERVAL_US) {
usleep(MAX_CHECK_INTERVAL_US);
pauseUs -= MAX_CHECK_INTERVAL_US;
} else {
usleep(pauseUs);
break;
}
// 休眠中途持续检测退出信号
checkExit();
}
// 收到退出信号,无限循环分段强制置进度100并跳出
if (exitPending() && part.loopCount == 0 && mCurrentInset >= mTargetInset && !part.enableFadeOut) {
if (lastProgress != 0 && lastProgress != 100) {
android::base::SetProperty(PROGRESS_PROP, "100");
continue;
}
break;
}
}
}
// 动画全部播放完成,批量释放循环帧纹理,释放GPU显存
for (const Animation::Part& part : animation.parts) {
if (part.loopCount != 1) {
for (const auto& frame : part.frames) {
glDeleteTextures(1, &frame.textureId);
}
}
}
SLOGD("%sAnimationShownTiming end time: %" PRId64 "ms",
mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
return true;
}
2.5 退出检测核心函数 checkExit ()
cpp
void BootAnimation::checkExit() {
ATRACE_CALL();
char propValue[PROPERTY_VALUE_MAX];
// 读取全局退出标记属性 service.bootanim.exit
property_get("service.bootanim.exit", propValue, "0");
int exitFlag = atoi(propValue);
if (exitFlag != 0) {
// 标记线程需要退出,下一轮循环执行淡出收尾
requestExit();
}
}
核心逻辑:每一帧渲染结束都会同步读取共享内存中的系统属性,一旦值为 1,标记线程退出,后续执行淡出过渡动画,不会直接强制杀进程,保证画面过渡顺滑。
三、开机动画退出完整链路:Launcher 就绪 → 设置退出属性
3.1 起点:Launcher 主线程 Idle 回调上报 activityIdle
Launcher 作为系统首个前台 Activity,完成 onResume、布局渲染后,ActivityThread 消息队列无待处理 UI 任务,触发 Idler 空闲处理器。
文件路径:frameworks/base/core/java/android/app/ActivityThread.java
java
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
final ActivityClient ac = ActivityClient.getInstance();
// 批量上报所有Resume完成的页面空闲状态
while (mNewActivities.size() > 0) {
final ActivityClientRecord record = mNewActivities.remove(0);
if (localLOGV) {
Slog.v(TAG, "Report idle activity " + record
+ " finished=" + (record.activity != null && record.activity.mFinished));
}
// 页面有效则跨Binder通知ATMS页面进入空闲
if (record.activity != null && !record.activity.mFinished) {
ac.activityIdle(record.token, record.createdConfig, stopProfiling);
record.createdConfig = null;
}
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
// 返回false,执行一次后销毁,不重复触发
return false;
}
}
触发时机:handleResumeActivity() 末尾通过 Looper.myQueue().addIdleHandler(new Idler()) 注册。
3.2 客户端 Binder 转发 ActivityClient.java
java
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
try {
// 调用IActivityTaskManager Binder接口,转发至系统端ActivityClientController
getActivityTaskManager().activityIdle(token, config, stopProfiling);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
3.3 系统端接收入口 ActivityClientController.java
路径:frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java
java
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origCallingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
// 根据页面Token查询服务端Activity记录
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) return;
// 转发至任务栈调度核心处理空闲逻辑
mTaskSupervisor.activityIdleInternal(r, false, false, config);
// 按需关闭性能采样器
if (stopProfiling && r.hasProcess()) {
r.app.clearProfilerIfNeeded();
}
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origCallingId);
}
}
3.4 开机完成判定 ActivityTaskSupervisor.java
java
void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
// 省略页面暂停、内存回收、栈调度逻辑
// 核心判定:系统仍在开机 且 所有前台Resume页面全部空闲
if ((mService.isBooting() && mRootWindowContainer.allResumedActivitiesIdle())
|| fromTimeout) {
checkFinishBootingLocked();
}
// 清除页面重启标记
r.mRelaunchReason = RELAUNCH_REASON_NONE;
}
private void checkFinishBootingLocked() {
boolean isBooting = mService.isBooting();
boolean enableScreen = false;
mService.setBooting(false);
// 首次完整开机,标记开机完成
if (!mService.isBooted()) {
mService.setBooted(true);
enableScreen = true;
}
if (isBooting || enableScreen) {
// 投递开机收尾任务至ATMS主线程Handler
mService.postFinishBooting(isBooting, enableScreen);
}
}
关键判定 mRootWindowContainer.allResumedActivitiesIdle():遍历全局所有前台 Activity,必须全部完成渲染、进入空闲状态,才判定整机开机流程结束,避免动画提前消失导致黑屏。
3.5 ATMS 主线程分发任务 ActivityTaskManagerService.java
java
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mH.post(() -> {
if (finishBooting) {
// 通知AMS完成开机,发送BOOT_COMPLETED广播、调度后台进程
mAmInternal.finishBooting();
}
if (enableScreen) {
// 联动WMS执行屏幕启用、关闭开机动画
mInternal.enableScreenAfterBoot(isBooted());
}
});
}
public void enableScreenAfterBoot(boolean booted) {
writeBootProgressEnableScreen(SystemClock.uptimeMillis());
// 调用WindowManagerService核心方法
mWindowManager.enableScreenAfterBoot();
synchronized (mGlobalLock) {
updateEventDispatchingLocked(booted);
}
}
3.6 WMS 设置退出属性 WindowManagerService.java
java
public void enableScreenAfterBoot() {
synchronized (mGlobalLock) {
if (mSystemBooted) return;
mSystemBooted = true;
hideBootMessagesLocked();
mPolicy.systemBooted();
// 执行关闭开机动画核心逻辑
performEnableScreen();
}
}
private void performEnableScreen() {
synchronized (mGlobalLock) {
// 省略屏幕亮度、输入设备初始化代码
if (!mBootAnimationStopped) {
// 核心:写入系统属性,通知BootAnimation退出
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
}
}
3.7 SystemProperties 底层机制:Java → JNI → 属性共享内存
Java 层接口 SystemProperties.java
java
public static void set(@NonNull String key, @Nullable String val) {
if (val != null && !key.startsWith("ro.") &&
val.getBytes(StandardCharsets.UTF_8).length > PROP_VALUE_MAX) {
throw new IllegalArgumentException("Property value too long");
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
// 调用native底层方法
native_set(key, val);
}
JNI 层实现 android_os_SystemProperties.cpp
cpp
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)
{
ScopedUtfChars key(env, keyJ);
if (!key.c_str()) return;
std::optional<ScopedUtfChars> value;
if (valJ != nullptr) {
value.emplace(env, valJ);
if (!value->c_str()) return;
}
const char* valueStr = value ? value->c_str() : "";
errno = 0;
// 调用bionic库底层属性设置函数
bool success = !__system_property_set(key.c_str(), valueStr);
if (!success) {
if (errno != 0) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
"set prop %s=%s failed: %m", key.c_str(), valueStr);
} else {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
"set prop %s=%s failed, check logcat", key.c_str(), valueStr);
}
}
}
底层属性系统原理
__system_property_set是 Bionic C 库提供接口,通过 Unix 域 Socket 向 PID=1 的 init 进程发送修改请求;- init 进程校验权限后,修改全局共享内存
/dev/__properties__内属性值; - 所有进程(包含独立的 bootanimation 进程)都会 mmap 这块共享内存,
property_get()直接内存读取,无需跨 Binder,读取速度极快,适合每帧高频轮询; - BootAnimation 在
checkExit()直接读取共享内存,实时感知service.bootanim.exit变更。
四、四大核心问题完整解答
1. 开机动画是谁启动的?
init 进程启动 SurfaceFlinger,SF 图形服务初始化完成后,通过 ctl.start bootanim 属性触发 init 拉起 /system/bin/bootanimation 独立 Native 进程。 进程配置在 bootanim.rc,默认 disabled,无法自启动,完全依赖 SurfaceFlinger 触发。
2. 为什么 Framework 未完全启动,动画就能显示?
BootAnimation、SurfaceFlinger 均为独立 Native 进程,运行在 Linux 用户态,不依赖 system_server、AMS、WMS 等 Java 层 Framework 服务。 仅需依赖底层图形硬件、EGL、BufferQueue 即可创建图层、渲染画面,因此在 Zygote、system_server 启动前就能播放动画。
3. SurfaceFlinger 和 BootAnimation 是什么关系?
- 依赖关系:BootAnimation 创建 Surface 图层、EGL 窗口、分配 GraphicBuffer 全部依赖 SurfaceFlinger,无 SF 则无法输出画面;
- 合成关系:BootAnimation 向 SF 提交专属不透明图层 Layer,SF 按最高 Z 序合成至屏幕;
- 启动时序:SF 先启动并初始化硬件,再主动拉起 BootAnimation;
- 进程隔离:两者是独立进程,通过 Binder 完成图层管理通信。
4. BootAnimation 如何知道什么时候退出?
- Launcher 全部加载、主线程空闲后,通过
activityIdle上报 ATMS; - ATMS 校验所有前台页面全部空闲,判定开机完成;
- WMS 调用
SystemProperties.set("service.bootanim.exit", "1")修改全局共享内存属性; - BootAnimation 每帧渲染末尾调用
checkExit()读取该属性,检测到 1 后开启淡出动画,播放完毕释放全部图形资源,进程退出。
5. 动画结束后 Launcher 如何接管屏幕?
- BootAnimation 进程销毁,对应的 BootAnimation 专属 Layer 从 SurfaceFlinger 移除;
- Launcher 的 Window 由 WMS 管理,图层 Z 序低于动画图层,动画销毁后 SF 自动合成 Launcher 窗口、状态栏、导航栏图层;
- 多图层硬件合成(HWC)直接输出桌面画面至显示屏,无黑屏断层。
五、完整全链路时序总表
- Kernel 启动 → init 进程初始化属性共享内存;
- init 启动 SurfaceFlinger 图形服务;
- SF 初始化 EGL、显示硬件完成,设置
ctl.start bootanim; - init 拉起 bootanimation 二进制进程;
- bootanimation_main 初始化 Binder 线程池,阻塞等待 SF 就绪;
- BootAnimation::run 创建子线程,先执行 readyToRun 初始化多屏幕、EGL、Surface;
- 进入 threadLoop → movie → playAnimation 逐帧循环渲染,每帧调用 checkExit;
- init 启动 Zygote,fork system_server,启动 AMS/WMS/ATMS;
- AMS 拉起 Launcher 桌面 Activity;
- Launcher onResume 完成,主线程 Idle 上报 activityIdle;
- ATMS 判定所有前台页面空闲,投递任务至 WMS;
- WMS 设置
service.bootanim.exit=1; - BootAnimation 检测到退出标记,执行淡出动画,播放完毕释放 EGL/Surface 资源;
- bootanimation 进程销毁,SF 移除动画图层,Launcher 窗口图层正常合成显示桌面。