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 小时前
RK3568 Android 13 开机时间优化-禁用非必要系统服务
android
Android-Flutter2 小时前
Java Thread 和 Runnable 详解
android
mmsx2 小时前
osmdroid Overlay 体系详解:比例尺/罗盘/总绘图层有序叠放
android·源码·地图·osmdroid
又见情义4 小时前
RK3568 Android 13 开机时间优化-HAL 服务延时启动
android
v_34836087625 小时前
Android8 开机动画结束分析
android
美狐美颜SDK开放平台6 小时前
直播APP开发核心功能详解:美颜SDK、音视频、礼物等功能成本怎么计算?
android·人工智能·计算机视觉·音视频·直播美颜sdk
安河桥。7 小时前
Android视频流处理模块硬件加速器数据流转技术说明
android·图像处理·isp
hunterandroid7 小时前
[Android 从零到一] APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅
android·前端