Android开机动画关闭流程

一步一图项目上要加一个开机动画结束的回调,我这边看下如何加

好,老规矩,如何启动动画?动画是谁启动的?怎么关闭的?谁通知关闭的

带着问题看源码

动画的启动流程

开机动画的主入口在哪?

这个main.cpp就是主入口

那么是谁通知它启动的?

Android系统在启动SystemServer进程时,通过两个阶段来启动系统所有服务,在第一阶段启动本地服务,如SurfaceFlinger,SensorService等,在第二阶段则启动一系列的Java服务。开机动画是在什么时候启动的呢?通过查看源码,Android开机动画是在启动SurfaceFlinger服务时启动的。SystemServer的main函数首先调用init1来启动本地服务,init1函数通过JNI调用C语言中的system_init()函数来实现服务启动。

那么当SurfaceFliger启动之后 会调用init函数

java 复制代码
void SurfaceFlinger::init()
{
    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.showupdates", value, "0");
    mDebugRegion = atoi(value);
#ifdef DDMS_DEBUGGING
    property_get("debug.sf.ddms", value, "0");
    mDebugDDMS = atoi(value);
    if (mDebugDDMS) {
        DdmConnection::start(getServiceName());
    }
#endif
    property_get("ro.bootmode", value, "mode");
    if (!(strcmp(value, "engtest")
        && strcmp(value, "special")
        && strcmp(value, "wdgreboot")
        && strcmp(value, "unknowreboot")
        && strcmp(value, "panic"))) {
        SurfaceFlinger::sBootanimEnable = false;
    }
}

然后一系列调用,当surfaceFliger 先启动一个线程,进行初始化然后当初始化结束之后调用 startBootAnim

java 复制代码
int Thread::_threadLoop(void* user)
{
    省略一车代码
    Thread* const self = static_cast<Thread*>(user);
    
            self->mStatus = self->readyToRun();
            
}

status_t SurfaceFlinger::readyToRun()
{
    初始化代码,本次不关心
    mReadyToRunBarrier.open();
    // start boot animation
    startBootAnim();
    return NO_ERROR;
}

当显示系统初始化完毕后,调用startBootAnim()函数来显示开机动画。

java 复制代码
void SurfaceFlinger::startBootAnim() {
    // start boot animation
    if(SurfaceFlinger::sBootanimEnable){
        property_set("service.bootanim.exit", "0");
        property_set("ctl.start", "bootanim");
    }
}

该进程对应的源码位于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线程池,用于接收其他进程的请求 
        sp<ProcessState> proc(ProcessState::self());

        ProcessState::self()->startThreadPool();

        // create the boot animation object (may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());

        waitForSurfaceFlinger();

        boot->run("BootAnimation", PRIORITY_DISPLAY);

        ALOGV("Boot animation set up. Joining pool.");
		//将当前线程注册到Binder线程池中

        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

在构造BootAnimation对象时,会调用onFirstRef函数。

cpp 复制代码
void BootAnimation::onFirstRef() {

    if (err == NO_ERROR) {
         省略无用代码
        preloadAnimation();
        
    }
}

先选择需要播放的动画,然后播放

播放路径有很多,这地方很简单,感兴趣可以看一下

cpp 复制代码
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";

上面是路径

java 复制代码
bool BootAnimation::preloadAnimation() {
    选择路径 会循环直到找到,如果没有我理解会加载一个默认值
    findBootAnimationFile();
    if (!mZipFileName.isEmpty()) {
        播放动画
        mAnimation = loadAnimation(mZipFileName);
        return (mAnimation != nullptr);
    }

    return false;
}

该函数首先为SurfaceComposerClient对象注册Binder死亡通知,然后调用BootAnimation的run方法,由于BootAnimation同时继承于Thread类,前面介绍SurfaceFlinger时已经介绍到,当某个类继承于Thread类时,当调用该类的run函数时,函数首先会执行readyToRun()函数来完成线程执行前的一些工作,然后线程反复执行threadLoop()函数,在BootAnimation类中,同样重新了这两个方法

java 复制代码
bool BootAnimation::threadLoop()
{
    bool r;
	//如果mAndroidAnimation为true,表示动画文件不存在,则显示Android滚动字样
    if (mAndroidAnimation) {
        r = android();
    } else {//显示动画
        r = movie();
    }
	//资源回收
    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    IPCThreadState::self()->stopProcess();
    return r;
}

开机画面主要是由一个zip格式的压缩包bootanimation.zip组成,压缩包里面包含数张png格式的图片,还有一个desc.txt的文本文档,开机时按desc.txt里面的指令,屏幕上会按文件名称顺序连续的播放一张张的图片,就像播放原始的胶带影片一样,形成动画。desc.txt是一个保存形式为ANSI格式的文件,用于设置这个动画像素(大小),帧数,闪烁次数,文件夹名称等。内容如下:

480 854 10

p 1 2 folder1

p 0 2 folder2

课代表总结,SurfaceFliger在初始化第一轮由init.rc启动,初始化结束然后进行启动动画播放,新起一个线程循环播放我们的动画,按顺序获取动画路径,也就是可以做拦截

OK

那么看下结束流程

一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。

由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。

在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。

结束流程是一堆调用链,所以总结下

结束是Activity idle机制进行 也就是主线程空闲通知ATP 一堆调用链先放开机广播,然后进行调用让SurfaceFliger通知动画结束最后通知AMS

如何关闭动画的?

动画是启动了一个线程进行循环播放,关闭就是中断了这个线程,结束掉播放

相关推荐
拭心11 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王14 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡14 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道14 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库15 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道16 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe16 小时前
Android Hook - 动态加载so库
android
居居飒16 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He19 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗20 小时前
Android笔试面试题AI答之Android基础(1)
android