android12 开机动画分析

Android 开机动画是一个由init进程触发、 SurfaceFlinger服务启动、 bootanimation进程负责播放的系统服务。它的生命周期可以被清晰地分为三个阶段:启动触发、动画播放、结束退出

启动触发:由SurfaceFlinger服务拉起

  1. 服务配置 :开机动画被定义为一个名为 bootanim 的服务,其配置文件是 bootanim.rc 。这个服务被标记为 disabledoneshot,意味着它不会开机自启,且退出后不会重启。
sql 复制代码
 service bootanim /system/bin/bootanimation
     class core animation
     user graphics
     group graphics
     disabled
     oneshot
  1. 核心启动者 :真正触发动画启动的,是负责图形合成的核心系统服务 SurfaceFlingerSurfaceFlinger 在启动过程中,会通过一个专门的线程 (StartPropertySetThread) 设置系统属性 ctl.startbootanim,从而触发 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进程的工作

  1. 程序入口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
}
  1. 核心工作 :程序会创建一个 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;
}
  1. 资源加载与解析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;
}

结束退出:响应系统就绪信号

  1. 桌面(Launcher)就绪,发出"空闲"信号 :当桌面的根Activity完成绘制并处于空闲状态时,它的主线程会通过 MessageQueue.IdleHandler 回调机制,向 ActivityManagerService (AMS) 发送一个 activityIdle 通知。
  2. 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();
    ...
}
  1. 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);
    }
   ...
}
  1. 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");
    ...

        
}
  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),但不会立即停止线程

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui