一份从原理到实践的 Android 开机动画知识库(Alight motion制作开机动画)

📚 目录

  1. BootAnimation 核心概念

  2. 源码分析:bootanimation_main.cpp

  3. 开机动画制作教程

  4. Android 版本差异

  5. 调试与排错

  6. 常见问题 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: 动画播放完成后黑屏?

原因

  1. 动画播放完成后没有保持最后一帧

  2. 图片数量不足,提前播放完毕

解决:确保动画播放到系统发送退出信号,或添加最后一帧停留

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 设备

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐