android9.0开机动画流程学习

目录

 1、源码获取与功能介绍

2、Android系统如何启动bootanimation

3、Bootanimation

3.1、初始化工作

 3.2、android()

3.3、movie()

3.3、停止播放

4、待补充


 1、源码获取与功能介绍

先看android9.0的开机动画的源代码,在Android OS 在线源代码网站:https://www.androidos.net.cn/sourcecode选择自己生产环境的android版本,就可以看到这个版本的所有源代码:

 开机动画相关的代码路径位于/frameworks/base/cmds/bootanimation目录下,可以选择下载下来用工具阅读。

bootanimation模块重要的便是如下几个文件:

  • bootanimation_main.cpp    // 该模块的主程序入口
  • Bootanimation.cpp             // 该模块的核心代码
  • Android.mk                        // 该模块的编译规则,在对系统进行全部编译的时候,会首先包含这个mk文件,然后等到编译这个模块时,根据mk文件中的定义,编译出指定的目标内容。
  • bootanim.rc                       // rc文件,用于init主进程拉起该模块

Android.mk文件部分内容如下:

…………
# bootanimation executable
# =========================================================
…………
LOCAL_MODULE:= bootanimation

LOCAL_INIT_RC := bootanim.rc

ifdef TARGET_32_BIT_SURFACEFLINGER
LOCAL_32_BIT_ONLY := true
endif

include $(BUILD_EXECUTABLE)


# libbootanimation
# ===========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := libbootanimation
…………
include ${BUILD_SHARED_LIBRARY}

其中LOCAL_MODULE:= bootanimation 和 include $(BUILD_EXECUTABLE)和语句意味着会编译出来一个bootanimation的可执行文件,这个可执行文件位于目标设备/system/bin目录下。而LOCAL_MODULE := libbootanimation和 include ${BUILD_SHARED_LIBRARY}也就代表着会编译出libbootanimation的调用库,在设备的/system/lib目录下,会看到一个libbootanimation.so库。

这个mk文件里还有个特殊的语句:LOCAL_INIT_RC := bootanim.rc

这个语句对应了这个模块下的bootanim.rc文件,在设备的/system/etc/init/目录下也会看到这个bootanim.rc文件(原因:编译的时候会直接copy过去):

service bootanim /system/bin/bootanimation    //将该bin文件“挂载”到bootanim服务中,可以通过拉起bootanim服务,以执行该bin文件
    class core animation                      //给服务指定为core和animation,这样方便操作多个服务同时启动或停止
    user graphics                             //在执行此服务之前先切换用户名,当前默认root
    group graphics audio                      //切换用户组
    disabled                                  //服务不会自动运行,必须显式地通过服务器来启动
    oneshot                                   //当此服务退出时不会自动重启
    writepid /dev/stune/top-app/tasks         //往指定的文件写内容

关于rc文件的语法和LOCAL_INIT_RC可以参考:

后面提到的其他路径的模块代码,都可以从该网站的文件树中获取到。

2、Android系统如何启动bootanimation

关于开机三个画面的流程,已经有大佬写的很详尽了,可以参考老罗的《Android系统的开机画面显示过程分析》一文,开机动画流程,这一部分大部分是借鉴该文,但由于版本不同,9.0版本代码位置和逻辑会有些差异。 第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面,在默认情况下,这个画面是不会出现的。第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。第三个开机画面是在系统服务启动的过程中出现的,它是一个动态的画面,就是使用比较多的bootanimation模块。无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。

我对开机动画一开始也是什么也不了解,也是通过看网上的博客慢慢了解这个过程,所以也是非常感谢各位博主的分享,文章中会出现和一些博主相同的部分,我会尽可能放上原文链接。

这里借用一下博客《一篇文章看明白 Android 图形系统 Surface 与 SurfaceFlinger 之间的关系》中的图文说明:

android从开机到显示桌面应用流程

如图,android9.0开机加载init进程的过程中会根据init.rc中的定义去启动SurfaceFlinger服务,9.0中该模块的rc文件规则是单独放在SurfaceFlinger模块下的。其在9.0中的代码路径:

/frameworks/native/services/surfaceflinger

该目录下有一个surfaceflinger.rc文件(低版本的rc语句都统一放在init.rc文件中,后来为了避免init.rc过于臃肿,便于模块维护和开发,一些服务会有单独的rc文件):

service surfaceflinger /system/bin/surfaceflinger
    class core animation
    user system
    group graphics drmrpc readproc
    onrestart restart zygote
    writepid /dev/stune/foreground/tasks
    socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
    socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
    socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0

init进程会根据这个rc文件规则,去启动位于目标设备/system/bin/surfaceflinger的可执行文件,该可执行文件的程序入口是surfaceflinger模块的main_surfaceflinger.cpp文件,其main函数:

int main(int, char**) {
    …………

    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();

    …………

    // initialize before clients can connect
    flinger->init();

    …………

    // run surface flinger in this thread
    flinger->run();

    return 0;
}

其中的sp<SurfaceFlinger> flinger = new SurfaceFlinger()通过智能指针的方式去创建SurfaceFlinger对象,因为SurfaceFlinger重写了父类RefBase的成员函数onFirstRef,因此,在创建该对象是会去执行SurfaceFlinger类中的onFirstRef函数:

void SurfaceFlinger::onFirstRef()
{
    mEventQueue->init(this);
}

关于mEventQueue,在SurfaceFlinger.h文件中有定义:

mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};

然后可以找到SurfaceFlinger模块下的MessageQueue.cpp文件中的init函数,其创建并初始化了handle:

void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
    mFlinger = flinger;
    mLooper = new Looper(true);
    mHandler = new Handler(*this);
}

回到上面main_surfaceflinger的main函数,可以看到语句flinger->init(),其调用的便是SurfaceFlinger的成员函数init(),部分代码如下:

// Do not call property_set on main thread which will be blocked by init Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
…………
 if (getHwComposer().hasCapability(
            HWC2::Capability::PresentFenceIsNotReliable)) {
        mStartPropertySetThread = new StartPropertySetThread(false);
    } else {
        mStartPropertySetThread = new StartPropertySetThread(true);
    }

    if (mStartPropertySetThread->Start() != NO_ERROR) {
        ALOGE("Run StartPropertySetThread failed!");
    }
…………

}

根据函数的注释也能看到,这里为了避免设置系统属性时引起阻塞,启动了一个StartPropertySetThread单独线程, 该线程定义如下:

StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
        Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}

status_t StartPropertySetThread::Start() {
    return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}

bool StartPropertySetThread::threadLoop() {
    // Set property service.sf.present_timestamp, consumer need check its readiness
    property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
    // Clear BootAnimation exit flag
    property_set("service.bootanim.exit", "0");
    // Start BootAnimation if not started
    property_set("ctl.start", "bootanim");
    // Exit immediately
    return false;
}

线程体中通过设置系统控制属性ctl.start为bootanim,来启动前面bootanim.rc中定义的bootanim服务:service bootanim /system/bin/bootanimation,以执行bootanimation可执行文件。而设置系统属性service.bootanim.exit为0,则是为了后面终止bootanimation程序做准备。

属性机制的坑已经填了,请见博客《Android4.4 property机制学习》

//TODO2    关于SurfaceFlinger,只是根据网上别人的博客,追踪了一下开机动画的相关内容,具体的内容与原理,继续挖坑。

3、Bootanimation

3.1、初始化工作

此处借鉴《[Android5.1]开机动画显示工作流程分析》一文,启动bootanimation可执行文件后,首先会执行到上面介绍过的bootanimation_main.cpp文件中的main函数:

int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

    bool noBootAnimation = bootAnimationDisabled();
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {

        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        waitForSurfaceFlinger();

        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
        ALOGV("Boot animation set up. Joining pool.");

        IPCThreadState::self()->joinThreadPool();
    }
    ALOGV("Boot animation exit");
    return 0;
}

其中的bootAnimationDisabled()函数会检测系统属性,以获取是否播放开机动画的属性值并赋值给noBootAnimation,如果noBootAnimation 为false,则直接退出程序,不进行播放开机动画。否则接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象。这个BootAnimation对象就是用来显示第三个开机画面的。由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信,因此,应用程序bootanimation就需要启动一个Binder线程池。

 BootAnimation类部分声明如下:

class BootAnimation : public Thread, public IBinder::DeathRecipient    //继承了Thread类和IBinder::DeathRecipient类
{
…………

private:
    virtual bool        threadLoop();    //线程体,如果返回true,且requestExit()没有被调用,则该函数会再次执行;如果返回false,则threadloop中的内容仅仅执行一次,线程就会退出
    virtual status_t    readyToRun();    //线程体执行前的初始化工作
    virtual void        onFirstRef();    //属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当有sp包装的类初始化的时候调用    
    virtual void        binderDied(const wp<IBinder>& who);    //当对象死掉或者其他情况导致该Binder结束时,就会回调binderDied()方法
…………
}

BootAnimation类的构造函数如下:

BootAnimation::BootAnimation(sp<Callbacks> callbacks)
        : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) {
    mSession = new SurfaceComposerClient();

    std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
    if (powerCtl.empty()) {
        mShuttingDown = false;
    } else {
        mShuttingDown = true;
    }
}

其中 mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信。SurfaceComposerClient类内部有一个实现了ISurfaceComposerClient接口的Binder代理对象mClient,这个Binder代理对象引用了SurfaceFlinger服务,SurfaceComposerClient类就是通过它来和SurfaceFlinger服务通信的。由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知,并执行binderDied函数:

void BootAnimation::binderDied(const wp<IBinder>&)
{
    // woah, surfaceflinger died!
    ALOGD("SurfaceFlinger died, exiting...");

    // calling requestExit() is not enough here because the Surface code
    // might be blocked on a condition variable that will never be updated.
    kill( getpid(), SIGKILL );
    requestExit();
}

binderDied函数会杀死当前进程,并退出下面讲到的onFirstRef函数所启动的bootAnimation线程:

run("BootAnimation", PRIORITY_DISPLAY);

由于BootAnimation类间接继承了RefBase类,且上面的main函数创建BootAnimation对象的时候使用智能指针引用,所以执行BootAnimation类的构造函数创建对象时,也会执行onFirstRef函数,下面是onFirstRef函数:

void BootAnimation::onFirstRef() {
    status_t err = mSession->linkToComposerDeath(this);
    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
    if (err == NO_ERROR) {
        run("BootAnimation", PRIORITY_DISPLAY);
    }
}

这里通过调用成员变量mSession的成员函数linkToComposerDeath,来注册SurfaceFlinger服务的死亡接收通知。并且调用了父类Thread的成员函数run来创建BootAnimation线程,由于该类重写了Thread类的readyToRun函数,所以在执行threadLoop之前,会先执行readyToRun来做一些初始化工作。其部分代码如下:

    // If the device has encryption turned on or is in process of being encrypted we show the encrypted boot animation.
    char decrypt[PROPERTY_VALUE_MAX];
    property_get("vold.decrypt", decrypt, "");

    bool encryptedAnimation = atoi(decrypt) != 0 ||
        !strcmp("trigger_restart_min_framework", decrypt);

    if (!mShuttingDown && encryptedAnimation) {
        static const char* encryptedBootFiles[] =
            {PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE};
        for (const char* f : encryptedBootFiles) {
            if (access(f, R_OK) == 0) {
                mZipFileName = f;
                return NO_ERROR;
            }
        }
    }
    static const char* bootFiles[] =
        {PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
    static const char* shutdownFiles[] =
        {PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};

    for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
        if (access(f, R_OK) == 0) {
            mZipFileName = f;
            return NO_ERROR;
        }
    }
    return NO_ERROR;

 首先会根据系统属性vold.decrypt来判断系统是否启动加密或者正在加密处理,如果是,则会根据优先级降级的方式,去播放如下两个路径的加密动画:

static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";

否则,将会根据Bootanimation构造函数中获取到的系统属性sys.powerctl判断系统是否正在关机,系统关机或非关机状态下依然根据优先级降序。这里的非关机状态是指,系统会在启动过程中或者启动后,都是可以通过运行bootanimation程序来播放开机动画。播放指定路径下的动画文件:

static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
…………
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip"; 

初始化工作完成后,就会进入下面的线程体函数threadLoop(),其中android()和movie()函数会返回false,即threadLoop()函数会返回false,那么线程体执行一次就会退出。

bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    如果初始化过程中能获取到动画文件,就播放该自定义动画,否则将执行android()函数,播放android原生动画
    if (mZipFileName.isEmpty()) {
        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);
    eglReleaseThread();
    IPCThreadState::self()->stopProcess();
    return r;
}

 3.2、android()

 根据readyToRun()函数中的初始化结果,如果不存在自定义的动画文件,threadLoop()就会执行android()函数,其部分代码如下:

bool BootAnimation::android()
{
    ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");

    mCallbacks->init({});

    // clear screen
    glShadeModel(GL_FLAT);
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);
    glClearColor(0,0,0,1);
    glClear(GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

    …………
    const nsecs_t startTime = systemTime();
    do {
        nsecs_t now = systemTime();
        …………

        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);

        checkExit();
    } while (!exitPending());

    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}

android()原生的开机动画位于/frameworks/base/core/res/assets/images目录下,由android-logo-shine.png和android-logo-mask.png两张png图片组成:

android-logo-mask.png
android-logo-shine.png

根据/frameworks/base/core/res/路径下的Android.bp文件:

android_app {
    name: "framework-res",
    no_framework_libs: true,
    certificate: "platform",

    // Soong special-cases framework-res to install this alongside
    // the libraries at /system/framework/framework-res.apk.

    // Generate private symbols into the com.android.internal.R class
    // so they are not accessible to 3rd party apps.
    aaptflags: [
        "--private-symbols",
        "com.android.internal",

        // Framework doesn't need versioning since it IS the platform.
        "--no-auto-version",

        // Allow overlay to add resource
        "--auto-add-overlay",
    ],

    // Create package-export.apk, which other packages can use to get
    // PRODUCT-agnostic resource data like IDs and type definitions.
    export_package_resources: true,
}

它们最终会编译成framework-res.apk并放在目标设备的/system/framework/目录下,而initTexture()函数会根据这两张图片来分别创建两个纹理对象,并存储在Bootanimation类的成员变量数组mAndroid中。通过混合渲染这两个纹理对象,我们就可以得到一个开机动画,这是通过中间的while循环语句来实现的。

图片android-logo-mask.png用作动画前景,它是一个镂空的“ANDROID”图像。图片android-logo-shine.png用作动画背景,它的中间包含有一个高亮的呈45度角的条纹。在每一次循环中,图片android-logo-shine.png被划分成左右两部分内容来显示。左右两个部分的图像宽度随着时间的推移而此消彼长,这样就可以使得图片android-logo-shine.png中间高亮的条纹好像在移动一样。另一方面,在每一次循环中,图片android-logo-shine.png都作为一个整体来渲染,而且它的位置是恒定不变的。由于它是一个镂空的“ANDROID”图像,因此,我们就可以通过它的镂空来看到它背后的图片android-logo-shine.png的条纹一闪一闪地划过。

3.3、movie()

如果存在自定义的动画文件,则会执行movie()函数,其部分代码如下:

bool BootAnimation::movie()
{
    Animation* animation = loadAnimation(mZipFileName);
    if (animation == NULL)
        return false;
    …………

    playAnimation(*animation);

    …………
    releaseAnimation(animation);

    …………
    return false;
}

其中 loadAnimation函数如下:

BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
{
   …………

    Animation *animation =  new Animation;
    animation->fileName = fn;
    animation->zip = zip;
    animation->clockFont.map = nullptr;
    mLoadedFiles.add(animation->fileName);

    parseAnimationDesc(*animation);
    if (!preloadZip(*animation)) {
        return NULL;
    }


    mLoadedFiles.remove(fn);
    return animation;
}

loadAnimation函数中的parseAnimationDesc函数,会去解析bootanimation*.zip压缩包中的desc.txt文件。举一个desc.txt文件的例子:

bootanimation*.zip文件结构

681 300 20
p 1 0 android
p 0 10 loading

如上desc.txt文件共三行,第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps),剩余的每一行都用来描述一个动画片断,这些行一般要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。第一个数字表示一个片断的循环显示次数,如果它的值等于0,那么就表示无限循环地显示该动画片断。第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时间间隔是以一个帧的时间为单位的。文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。参考《[Android5.1]开机动画desc.txt描述文件的分析》

 以上面这个desc.txt文件的内容为例,它描述了一个大小为681 x 300 的开机动画,动画的显示速度为20帧每秒。这个开机动画包含有两个片断android和loading。片断android只显示一次,它对应的png图片保存在目录android中。片断loading无限循环地显示,其中,每两次循环显示的时间间隔为10 x (1 / 24)秒,它对应的png图片保存在目录loading中。

parseAnimationDesc函数根据desc.txt文件规则解析完成后,根据解析结果和那两个目录的图片资源,去执行preloadZip函数以进行预加载处理。解析和预加载这两个过程都是以一个Animation类型的实例animation为主体进行处理,其定义如下:

    struct Animation {
        struct Frame {
            String8 name;
            FileMap* map;
            int trimX;
            int trimY;
            int trimWidth;
            int trimHeight;
            mutable GLuint tid;
            bool operator < (const Frame& rhs) const {
                return name < rhs.name;
            }
        };
        struct Part {
            int count;  // The number of times this part should repeat, 0 for infinite
            int pause;  // The number of frames to pause for at the end of this part
            int clockPosX;  // The x position of the clock, in pixels. Positive values offset from
                            // the left of the screen, negative values offset from the right.
            int clockPosY;  // The y position of the clock, in pixels. Positive values offset from
                            // the bottom of the screen, negative values offset from the top.
                            // If either of the above are INT_MIN the clock is disabled, if INT_MAX
                            // the clock is centred on that axis.
            String8 path;
            String8 trimData;
            SortedVector<Frame> frames;
            bool playUntilComplete;
            float backgroundColor[3];
            uint8_t* audioData;
            int audioLength;
            Animation* animation;
        };
        int fps;
        int width;
        int height;
        Vector<Part> parts;
        String8 audioConf;
        String8 fileName;
        ZipFileRO* zip;
        Font clockFont;
    };

loadAnimation函数执行完成后就会去执行playAnimation函数进行播放动画,播放完成后到releaseAnimation函数释放相关资源。

3.4、停止播放

当SystemServer将系统中的关键服务启动完成后,会启动桌面启动器Launcher,Launcher启动后,最终以SurfaceFlinger服务接收到类型为IBinder::FIRST_CALL_TRANSACTION,即类型为BOOT_FINISHED的进程间通信请求时,它就会将该请求交给它的成员函数bootFinished来处理:

//SurfaceFlinger::bootFinished

// stop boot animation formerly we would just kill the process, but we now ask it to exit so it can choose where to stop the animation.
property_set("service.bootanim.exit", "1");

 可以看到其将系统属性service.bootanim.exit设置为1,而在前面的android()函数体和movie()函数的调用链中都可以找到checkExit()和exitPending()这两个函数:

void BootAnimation::checkExit() {
    // Allow surface flinger to gracefully request shutdown
    char value[PROPERTY_VALUE_MAX];
    property_get(EXIT_PROP_NAME, value, "0");    //static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
    int exitnow = atoi(value);
    if (exitnow) {
        requestExit();
        mCallbacks->shutdown();
    }
}

显而易见,checkExit函数在检测到该系统属性为1的时候,就会调用requestExit函数停止bootanimation的线程体threadLoop。而while循环中的循环判断条件表达式中的exitPending()函数,会去判断requestExit函数是否被调用过,如果调用过则返回true,否则为false,以终止android函数或movie函数中的while循环。

4、待补充

文章中关于系统属性、SurfaceFlinger、以及在过程中遇到的SELinux域转换的问题,都是比较复杂的模块,以后认真学习过后再单独写博文。本文中很多省略的代码主要是太菜了,还看不明白,所以后面了解地更深刻以后,会及时补充本博文。

猜你喜欢

转载自blog.csdn.net/GDUYT_gduyt/article/details/111633297