安卓framework学习3:修改开机动画
一份从原理到实践的 Android 开机动画知识库(Alight motion制作开机动画)
📚 目录
-
BootAnimation 核心概念
-
源码分析:bootanimation_main.cpp
-
开机动画制作教程
-
Android 版本差异
-
调试与排错
-
常见问题 FAQ
一、核心概念
1.1 BootAnimation 是什么?
| 直译 | 含义 |
|---|---|
| Boot + Animation | 启动 + 动画 |
在 Android 中的定义:
-
系统从启动到桌面出现之间显示的动态画面
-
是一个独立进程:
/system/bin/bootanimation -
核心类位于
frameworks/base/cmds/bootanimation/
1.2 动画类型对比
| 类型 | 表现形式 | 资源配置 | 适用场景 |
|---|---|---|---|
| 默认 Android Logo | 静态或简单的动态闪烁 | 代码绘制 | 无自定义动画的设备 |
| bootanimation.zip | 逐帧 PNG 动画 | ZIP 包 | 定制 ROM、品牌设备 |
| 关机动画 | 同上,但模式不同 | 同上,参数区分 | 关机/重启过程 |
二、源码分析
2.1 主入口:bootanimation_main.cpp
这是整个开机动画进程的入口和调度中心。
完整流程图
text
┌─────────────────────────────────────────────────────────────────┐ │ bootanimation_main.cpp │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 设置进程优先级 (PRIORITY_DISPLAY = -4) │ │ ↓ │ │ 2. 检测是否禁用动画 (bootAnimationDisabled) │ │ ↓ │ │ 3. 初始化 Binder 线程池 (ProcessState) │ │ ↓ │ │ 4. 创建 BootAnimation 对象 │ │ ├── 关机模式: new BootAnimation(..., true) │ │ └── 开机模式: new BootAnimation(..., false) │ │ ↓ │ │ 5. 等待 SurfaceFlinger 就绪 (waitForSurfaceFlinger) │ │ ↓ │ │ 6. 启动动画线程 (boot->run) │ │ ↓ │ │ 7. 主线程加入 Binder 池 (joinThreadPool) │ │ ↓ │ │ ┌────────────────────────────────────┐ │ │ │ BootAnimation::threadLoop() │ ← 动画播放主循环 │ │ │ ├── 加载 bootanimation.zip │ │ │ │ ├── 解析 desc.txt │ │ │ │ ├── 逐帧渲染 │ │ │ │ └── 播放完成/系统就绪 → 退出 │ │ │ └────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
关键代码段解读
cpp
// 1. 优先级设置 - 确保动画流畅
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
// ANDROID_PRIORITY_DISPLAY = -4,高于普通进程
// 2. 禁用检测 - 优化启动速度
bool noBootAnimation = bootAnimationDisabled();
// 检查属性: ro.boot.silentboot, sys.boot_completed 等
// 3. Binder 初始化 - 与 SurfaceFlinger 通信
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// 必须,否则无法获取显示缓冲区
// 4. 模式区分
if(argc > 1 && strcmp(argv[1], "shutdown") == 0) {
boot = new BootAnimation(..., true); // 关机动画
} else {
boot = new BootAnimation(..., false); // 开机动画
}
// 5. 等待依赖服务
waitForSurfaceFlinger(); // 阻塞直到 SurfaceFlinger 可用
// 6. 动画线程启动
boot->run("BootAnimation", PRIORITY_DISPLAY);
// 7. 主线程进入消息循环
IPCThreadState::self()->joinThreadPool();
// 翻译: "主线程正在加入 Binder 线程池,等待消息"
2.2 动画资源查找优先级
cpp
// BootAnimation.cpp static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip"; static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip"; static const char ODM_BOOTANIMATION_FILE[] = "/odm/media/bootanimation.zip"; static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
实际查找顺序:
text
1. /apex/com.android.bootanimation/etc/bootanimation.zip (Android 10+) 2. /product/media/bootanimation.zip (产品定制) 3. /odm/media/bootanimation.zip (硬件定制) 4. /system/media/bootanimation.zip (系统默认)
2.3 播放逻辑
cpp
// BootAnimation::threadLoop()
bool BootAnimation::threadLoop() {
bool result;
if (mZipFileName.isEmpty()) {
result = android(); // 播放默认 Android Logo
} else {
result = movie(); // 播放 bootanimation.zip
}
return result;
}
三、制作教程
3.1 文件结构
text
bootanimation.zip
├── desc.txt # 动画描述文件(必须)
├── audio.wav # 音频文件(可选,Android 10+)
├── part0/ # 第一段动画图片目录
│ ├── 0000.png
│ ├── 0001.png
│ └── ...
└── part1/ # 第二段动画图片目录(可选)
├── 0000.png
└── ...
3.2 desc.txt 完整语法
text
[宽度] [高度] [帧率] [类型] [播放次数] [暂停帧数] [图片目录] [类型] [播放次数] [暂停帧数] [图片目录] ...
参数详解
| 参数 | 取值范围 | 说明 |
|---|---|---|
| 宽度 | 正整数 | 每帧图片的宽度(像素) |
| 高度 | 正整数 | 每帧图片的高度(像素) |
| 帧率 | 1-60 | 每秒播放的帧数 |
| 类型 | p / c |
p=可被中断,c=必须完整播放 |
| 播放次数 | 0=无限,N=次数 | ⚠️ 0 只能用于最后一个 stage |
| 暂停帧数 | 0-N | 播完后暂停多少帧再继续 |
| 图片目录 | 字符串 | ZIP 内的子目录名 |
示例
单段动画(推荐):
text
540 960 30 p 1 0 part0
播放 30fps 的动画一次,结束后直接退出。
两段动画(品牌常见):
text
1080 1920 30 p 1 0 part0 # 品牌 Logo 动画,播一次 p 0 0 part1 # 无限循环,等待系统启动
三段动画(带延迟):
text
720 1280 24 p 1 0 part0 # 第一段 c 1 30 part1 # 必须完整播放,结束后暂停30帧 p 0 0 part2 # 无限循环
3.3 图片制作规范
| 规范项 | 要求 |
|---|---|
| 格式 | PNG(24位或32位) |
| 命名 | 5位数字序号:00000.png, 00001.png... |
| 分辨率 | 与 desc.txt 声明一致 |
| 压缩 | 打包时使用 zip -0(不压缩) |
| 数量 | 建议 30-90 帧(1-3秒动画) |
批量处理脚本(Python)
python
#!/usr/bin/env python3
# resize_anim.py - 批量缩放图片并重命名
from PIL import Image
import os
INPUT_DIR = "frames_original"
OUTPUT_DIR = "part0"
TARGET_WIDTH = 540
TARGET_HEIGHT = 960
SKIP_FRAME = 3 # 每3帧取1帧
os.makedirs(OUTPUT_DIR, exist_ok=True)
frames = sorted([f for f in os.listdir(INPUT_DIR) if f.endswith('.png')])
selected = frames[::SKIP_FRAME]
for i, filename in enumerate(selected):
img = Image.open(os.path.join(INPUT_DIR, filename))
img = img.resize((TARGET_WIDTH, TARGET_HEIGHT), Image.LANCZOS)
img.save(os.path.join(OUTPUT_DIR, f"{i:05d}.png"))
print(f"Processed: {filename} -> {i:05d}.png")
3.4 打包命令
bash
# 进入包含 desc.txt 和 part0 的目录 cd bootanimation_content # 不压缩打包(关键!) zip -0 -r ../bootanimation.zip * # 或使用存储模式 zip -r -0 ../bootanimation.zip .
3.5 音频配置(Android 10+)
desc.txt 中添加:
text
1080 1920 30 a boot_sound.wav p 1 0 part0
注意事项:
-
仅支持 WAV 格式(PCM,16bit,44100Hz)
-
音频文件放在 ZIP 根目录
-
音频会循环播放直到动画结束
-
部分 AOSP 版本移除了此功能
四、版本差异
4.1 Android 版本支持对比
| 特性 | Android 8/9 | Android 10 | Android 11 | Android 12+ |
|---|---|---|---|---|
| bootanimation.zip | ✅ | ✅ | ✅ | ✅ |
| 多 stage 动画 | ✅ | ✅ | ✅ | ✅ |
| 音频支持 | ❌ | ✅(部分) | ✅(部分) | ⚠️ 禁用 |
| APEX 路径 | ❌ | ✅ | ✅ | ✅ |
| 关机动画 | ✅ | ✅ | ✅ | ✅ |
| 静默启动 | ✅ | ✅ | ✅ | ✅ |
4.2 路径变化
| Android 版本 | 主要路径 |
|---|---|
| 8.x 及以下 | /system/media/bootanimation.zip |
| 9.x | /system/media/bootanimation.zip |
| 10+ | /product/media/bootanimation.zip 优先 |
| 11+ | /apex/com.android.bootanimation/... 作为备用 |
4.3 静默启动属性
bash
# 禁用开机动画(优化启动时间) adb shell setprop ro.boot.silentboot 1 # 查看当前状态 adb shell getprop ro.boot.silentboot
五、调试与排错
5.1 日志命令
bash
# 基础日志 adb logcat -s bootanimation # 详细日志(含帧率) adb logcat -s bootanimation:* -v time # 完整日志过滤 adb logcat | grep -E "bootanimation|SurfaceFlinger" # 实时监控(推荐) adb logcat -c && adb logcat -s bootanimation:* SurfaceFlinger:* -v color
5.2 手动测试命令
bash
# 手动启动开机动画 adb shell bootanimation # 停止当前动画 adb shell pkill bootanimation # 测试关机动画 adb shell bootanimation shutdown # 查看进程 adb shell ps | grep bootanimation
5.3 文件验证
bash
# 检查文件是否存在及权限 adb shell ls -lh /system/media/bootanimation.zip # 验证 ZIP 包完整性 adb shell unzip -t /system/media/bootanimation.zip # 查看 desc.txt adb shell unzip -p /system/media/bootanimation.zip desc.txt # 查看动画帧数 adb shell unzip -l /system/media/bootanimation.zip | grep -c "\.png$"
5.4 性能分析
bash
# 查看动画播放帧率 adb logcat -s bootanimation | grep -E "fps|frame" # 查看内存占用 adb shell dumpsys meminfo bootanimation # CPU 占用率 adb shell top -n 1 | grep bootanimation
5.5 常见错误码
| 日志内容 | 含义 | 解决方案 |
|---|---|---|
couldn't find any temp file |
ZIP 包为空或损坏 | 重新打包 |
failed to map file |
文件权限或大小问题 | 检查文件权限 |
invalid desc.txt |
desc.txt 格式错误 | 检查语法 |
no such file |
找不到动画文件 | 确认路径正确 |
out of memory |
图片分辨率过高 | 降低分辨率 |
六、常见问题 FAQ
Q1: 动画无限循环,无法进入桌面?
原因:desc.txt 中所有 stage 都设置了 p 0 0(无限循环)
解决:最后一个 stage 不要无限循环,或确保系统能中断它
diff
- p 0 0 part0 + p 1 0 part0
Q2: 动画播放完成后黑屏?
原因:
-
动画播放完成后没有保持最后一帧
-
图片数量不足,提前播放完毕
解决:确保动画播放到系统发送退出信号,或添加最后一帧停留
Q3: 推送后动画没变化?
检查清单:
bash
# 1. 确认文件路径正确 adb shell ls /system/media/bootanimation.zip # 2. 确认没有禁用动画 adb shell getprop ro.boot.silentboot # 应为空或0 # 3. 确认 SELinux 不阻止 adb shell dmesg | grep -i avc | grep bootanimation # 4. 重启动画进程 adb shell pkill bootanimation && adb shell bootanimation &
Q4: 分辨率太高导致启动慢?
原因:GPU 需要解码大量大尺寸图片,IO 压力大
解决:
text
原分辨率 1080x1920 → 推荐 540x960 原帧数 90 帧 → 推荐 30-45 帧 原图片压缩 → 使用 PNG 8-bit 索引色
Q5: Tablet 设备显示默认 Logo 而不是动画?
原因:某些设备(如 RK 平板)默认跳过 bootanimation.zip
解决:在 device.mk 中添加
makefile
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/bootanimation.zip:$(TARGET_COPY_OUT_SYSTEM)/media/bootanimation.zip
# 或强制启用
PRODUCT_PROPERTY_OVERRIDES += \
ro.config.bootanimation=1
Q6: 如何在编译时打包?
方法一:直接复制
makefile
# device/rockchip/rk356x/rk3566_r/rk3566_r.mk
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/bootanimation.zip:$(TARGET_COPY_OUT_SYSTEM)/media/bootanimation.zip
方法二:使用 PRODUCT_PACKAGES
makefile
# 创建 bootanimation.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := bootanimation LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_MEDIA) LOCAL_SRC_FILES := bootanimation.zip include $(BUILD_PREBUILT)
七、最佳实践总结
✅ 推荐做法
| 项目 | 推荐配置 | 理由 |
|---|---|---|
| 分辨率 | ≤ 540x960 | 平衡画质与性能 |
| 帧数 | 30-60 帧 | 1-2秒的动画时长 |
| 帧率 | 24-30 fps | 流畅且不耗资源 |
| 播放次数 | 最后一帧 p 1 0 | 可被中断,不阻塞启动 |
| 图片格式 | PNG-8 索引色 | 文件体积小 |
| 打包方式 | zip -0 -r |
不压缩,解码更快 |
❌ 避免事项
-
❌ 使用无限循环(
p 0 0)作为唯一 stage -
❌ 分辨率超过物理屏幕分辨率
-
❌ 使用过高的帧率(>60fps)
-
❌ 使用 JPEG 格式(没有 Alpha 通道,解码慢)
-
❌ 帧数过多(>150 帧会导致加载慢)
八、快速参考卡片
bash
# 一键测试脚本 adb root && adb remount adb push bootanimation.zip /system/media/ adb shell chmod 644 /system/media/bootanimation.zip adb reboot # 一键提取当前动画 adb pull /system/media/bootanimation.zip . # 一键查看动画信息 unzip -p bootanimation.zip desc.txt unzip -l bootanimation.zip | head -20
文档版本:v2.0
最后更新:2026-06-05
适用版本:Android 8.0 - 14.0
适用平台:RK3566、RK3588、通用 Android 设备
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)