Bootanimation | 开机动画整体流程解析

Android 开机动画实现方式目前主要方式:逐帧动画和OpenGL直接编程绘制动画

  • 逐帧动画 :逐帧动画的序列内容不一样,给制作增加了负担而且输出文件量很大,逐帧动画具有非常大的灵活性。实现原理是将一系列图片打包成bootanimation.zip放入/system/media/目录,系统会将图片一帧一帧循环播放形成一个动画效果,但是zip大于5M的时候,动画会有明显的卡顿,文件越大越不流畅。
  • OpenGL:OpenGL 是个定义一个跨编程语言、跨平台的应用程序接口 API 的规范,可以用于生成二维、三维图像。这个接口由近三百五十个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。

开机动画启动流程

先简单了解下系统的启动过程(后面会详细讲解),内核启动后会启动第一个进程,就是init进程,init 进程会根据init.rc 配置启动surfaceflinger进程,需要注意在Android系统中一切和屏幕绘制相关的都需要用到surfaceflinger,而bootanimation开机动画也属于屏幕绘制,所以bootanimation肯定在surfaceflinger进程启动之后,但是init.rc不会启动bootanim.rc ,因为bootanim.rc 中配置了disable 就不会启动bootanim,那么什么时候启动bootanim呢?

开机动画相关的代码:

  • bootanimation : frameworks/base/cmds/bootanimation/

看下bootanimation的源码目录:可以看到Android.bp的配置,整体的开机动画就是C++写的,同时还有bootanim.rc文件

看下Android.bp的核心配置:cc_binary 最终会编译成Native 执行程序

css 复制代码
cc_binary {
    name: "bootanimation",
    defaults: ["bootanimation_defaults"],

    shared_libs: [
        "libOpenSLES",
        "libbootanimation",
    ],

    srcs: [
        "BootAnimationUtil.cpp",

        "bootanimation_main.cpp",
        "audioplay.cpp",
    ],

    product_variables: {
        product_is_iot: {
            shared_libs: [
                "libandroidthings",
                "libandroidthings_protos",
                "libchrome",
                "libprotobuf-cpp-lite",
            ],
            static_libs: ["libjsoncpp"],
            srcs: [
                "iot/iotbootanimation_main.cpp",
                "iot/BootAction.cpp",
                "iot/BootParameters.cpp",
            ],
            exclude_srcs: [
                "bootanimation_main.cpp",
                "audioplay.cpp",
            ],
        },
    },

    init_rc: ["bootanim.rc"],
}

在out目录下的 su_os/system/bin目录下,就可以看到编译好的bootanimation的可执行程序

bootanim.rc : 由于配置了disabled那么在init.rc 执行的时候就不会拉起bootanim.rc

css 复制代码
service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    ioprio rt 0
    writepid /dev/stune/top-app/tasks
  • sufaceflinger : frameworks/native/services/surfaceflinger/ 一切和屏幕绘制有关的都和sufaceflinger相关,同时在源码目录中也会看到surfaceflinger.rc文件,会在init进程启动的时候启动sufaceflinger 进程
perl 复制代码
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 : system/core/init 开机启动的第一个进程init 进程

init进程会根据 init.rc 配置启动surfaceflinger 进程,那么看下surfaceflinger的main函数: 主要初始化了SurfaceFlinger实例,然后执行了initrun方法

ini 复制代码
   sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
   ......
   flinger->init();
   ......
   flinger->run();

看下init方法干了什么:初始化graphics之后,创建了StartPropertySetThread实例,从名字上翻译"开始属性设置线程",然后调用了Start()方法,线程运行起来了,播放开机动画.

scss 复制代码
 ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");
......
    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);

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

那么线程中干了什么呢:可以看到调用了run方法,线程运行起来了,threadLoop相当于线程中执行的方法,可以理解为java中的线程执行调用了start,就会执行runnable方法,这个runnablethreadLoop类似。

arduino 复制代码
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;
}

threadLoop 方法中进行了属性设置,可以看到bootanim,这里可以猜测调用了bootanim.rc

property_set 到底是干什么呢?为什么设置了一个属性就启动了呢?这里就需要看init进程的代码(/system/core/init/init.cpp),来看init.cpp中的SecondStageMain函数(注意AOSP10源码中,main函数位于main.cpp中):

scss 复制代码
......
property_init();
StartPropertyService(&epoll);
......

可以推测StartPropertyService方法中启动了属性服务,如下代码:可以看到创建了一个socket,而socket 可以用来跨进程通信 ,那么socket会一直监听属性设置的工作,那么就是在init进程中创建了一个属性服务的socket,用来和其他进程间进行通信 ,设置了属性就会监听到作出相应的动作处理(epoll 是一个多路复用的机制,监听fd有没有消息,有消息来就会调用回调的方法后面在详细讲解epoll机制)

ini 复制代码
void StartPropertyService(Epoll* epoll) {
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");

    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    }

    listen(property_set_fd, 8);

    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
        PLOG(FATAL) << result.error();
    }
}

当监听到有属性设置epoll就会触发property_set_fd有消息来,就会调用handle_property_set_fd的回调方法,如下代码:最终会调用HandlePropertySet方法去处理设置的属性

arduino 复制代码
static void handle_property_set_fd() {

.......
  switch (cmd) {
    case PROP_MSG_SETPROP: {
        //............
        uint32_t result =
            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
       //..............

        break;
      }
      .......
}

HandlePropertySet方法如下:首先判断属性名的前缀ctl. 控制的属性, 在surfaceflinger中的属性名是: property_set("ctl.start", "bootanim") 这里就会匹配到ctl.start 然后执行HandleControlMessage方法

c 复制代码
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
    if (StartsWith(name, "ctl.")) {
        HandleControlMessage(name.c_str() + 4, value, cr.pid);
        return PROP_SUCCESS;
    }
.......
    return PropertySet(name, value, error);
}

HandleControlMessage 方法如下:

传递的msg 的参数name.c_str() + 4 其实就是取属性名向后移动4位的字符串,那么ctl.start,移动4位就是start

name 参数对应的就是value: bootanim

arduino 复制代码
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
    const auto& map = get_control_message_map();
    const auto it = map.find(msg);
    Service* svc = nullptr;
    const ControlMessageFunction& function = it->second; //start
    switch (function.target) {
        case ControlTarget::SERVICE:
            svc = ServiceList::GetInstance().FindService(name);
            break;
        case ControlTarget::INTERFACE:
            svc = ServiceList::GetInstance().FindInterface(name);
            break;
        default:
            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
                       << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
            return;
    }
//...............
 if (svc == nullptr) {
        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
        return;
    }

    if (auto result = function.action(svc); !result) {
        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
    }
}

有个关键的的代码:function是什么?

ini 复制代码
    const auto& map = get_control_message_map();
    const auto it = map.find(msg);
    Service* svc = nullptr;
    const ControlMessageFunction& function = it->second; //start

get_control_message_map 方法如下其实就是map结构,msg = start,那么it 对应的就是{"start",{ControlTarget::SERVICE, DoControlStart}}

start对应{ControlTarget::SERVICE, DoControlStart}} 那么it→second 就是{ControlTarget::SERVICE, DoControlStart}}

c 复制代码
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
    // clang-format off
    static const std::map<std::string, ControlMessageFunction> control_message_functions = {
        {"sigstop_on",        {ControlTarget::SERVICE,
                               [](auto* service) { service->set_sigstop(true); return Success(); }}},
        {"sigstop_off",       {ControlTarget::SERVICE,
                               [](auto* service) { service->set_sigstop(false); return Success(); }}},
        {"start",             {ControlTarget::SERVICE,   DoControlStart}},
        {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
        {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
        {"interface_start",   {ControlTarget::INTERFACE, DoControlStart}},
        {"interface_stop",    {ControlTarget::INTERFACE, DoControlStop}},
        {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
    };
    // clang-format on

    return control_message_functions;
}

function对应的是一个结构体:

arduino 复制代码
struct ControlMessageFunction {
    ControlTarget target;
    std::function<Result<Success>(Service*)> action;
};

target ControlTarget::SERVICE action 就是DoControlStart 其实就是一个函数:调用了service→Start方法

scss 复制代码
static Result<Success> DoControlStart(Service* service) {
    return service->Start();
}

在回过头来看HandleControlMessage方法:function.target 对应的ControlTarget::SERVICE 就会执行从ServiceList中查找Service,name其实就是传递属性值也就是bootanim ,最终调用了function.action(svc),也就是调用了**service->Start()**方法执行启动了开机动画

arduino 复制代码
    switch (function.target) {
        case ControlTarget::SERVICE:
            svc = ServiceList::GetInstance().FindService(name);
            break;
        case ControlTarget::INTERFACE:
            svc = ServiceList::GetInstance().FindInterface(name);
            break;
        default:
            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
                       << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
            return;
    }
    ///执行
     if (auto result = function.action(svc); !result) {
        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
    }

ServiceList中为什么能找到bootanim呢?这里就要回看bootanim.rc文件:我们知道在执行init.rc的时候由于bootanim.rc 配置了disabled 属性并没有启动bootanim 服务,但是会把这个服务记录下来存放到ServieList中,手动启动

css 复制代码
service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    ioprio rt 0
    writepid /dev/stune/top-app/tasks

bootanimation 开机动画绘制图像,依赖于sufaceflinger 所以需要在surfacefligner 启动之后手动调用开机动画进程

详细的流程图总结如下所示:

开机动画结束流程

在上述中清晰的描述了开机动画的启动流程,那么什么时候结束开机动画呢?首先要去看bootanimation模块下的main方法,理清执行流程:

  1. 首先去查询是否禁用了开机动画,一般Android可以移植到其他设备中,可能有的设备不需要开机动画就会禁止掉
  2. 启动binder线程池,主要用于binder通信线程和sf(surfacefligner简写)跨进程通信
  3. 创建BootAnimation的实例,需要注意的是sp是一个智能指针
  4. waitForSurfaceFlinger 等待sf进程启动,涉及到屏幕绘制的都需要sf
  5. BootAnimation 继承了Thread 调用run方法运行线程,会执行 readyToRunthreadloop方法
scss 复制代码
int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
    bool noBootAnimation = bootAnimationDisabled();//开机动画有没有被禁止?
    if (!noBootAnimation) {
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();//启动线程池 主要binder通信线程和sf跨进程通信
        // create the boot animation object (may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());//sp 智能指针会执行onFirstRef
        waitForSurfaceFlinger();//等待sf进程启动
        boot->run("BootAnimation", PRIORITY_DISPLAY);//运行线程,执行 readyToRun 和 threadloop方法
        IPCThreadState::self()->joinThreadPool();
    }
    ALOGV("Boot animation exit");
    return 0;
}
BootAnimation 创建实例流程

首先分析BootAnimation的创建实例流程,需要注意的是 sp智能指针一般都会有onFirstRef方法 在初始化的时候都会执行onFirstRef方法:

在创建BootAnimation的实例时会先创建binder通信client端代理,然后调用preloadAnimation预加载开机动画文件

scss 复制代码
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
        : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
    mSession = new SurfaceComposerClient();//binder通信 client端的代理
    std::string powerCtl = android::base::GetProperty("sys.powerctl", "");//获取一个属性判断是开机还是关机
    if (powerCtl.empty()) {
        mShuttingDown = false;
    } else {
        mShuttingDown = true;
    }
}
void BootAnimation::onFirstRef() {//sp<>智能指针 一般都会有onFirstRef方法 在初始化的时候都会执行onFirstRef方法
    status_t err = mSession->linkToComposerDeath(this);
    if (err == NO_ERROR) {
      ////..............
        preloadAnimation();//预加载开机动画文件
     ////.................
    }
}

如下代码,主要将开机动画文件bootFiles以及关机动画文件 shutdownFiles进行查找并将文件名存储到了mZipFileName的变量中:

arduino 复制代码
bool BootAnimation::preloadAnimation() {
    findBootAnimationFile();
    if (!mZipFileName.isEmpty()) {
        mAnimation = loadAnimation(mZipFileName);
        return (mAnimation != nullptr);
    }
    return false;
}

void BootAnimation::findBootAnimationFile() {
    ......................
    const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
    //查找开机动画文件的路径
    static const char* bootFiles[] =
        {APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : 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;
        }
    }
}

从上述代码中可推断出开机动画的文件在哪里配置的:这些有助于自定义开机动画的zip配置

arduino 复制代码
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";
执行线程

由于BootAnimation继承了Thread 所以可以调用run方法开始线程执行

kotlin 复制代码
class BootAnimation : public Thread, public IBinder::DeathRecipient

肯定会执行 readyToRunthreadloop方法,先执行readyToRun方法:

主要是和sf跨进程通信构建一个画布,初始化opengl,用于在屏幕上绘制开机动画.

ini 复制代码
status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();//对assets文件的初始化

    mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();//跨进程通信 用sf获取屏幕的display信息
    if (mDisplayToken == nullptr)
        return -1;

    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
    if (status)
        return -1;

    //利用DisplayInfo  构建一个画布
    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

    SurfaceComposerClient::Transaction t;
    t.setLayer(control, 0x40000000)
        .apply();

    //获取一个画布 绘制
    sp<Surface> s = control->getSurface();

    // OPENGL 初始化
    // initialize opengl and egl
    const EGLint attribs[] = {
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_DEPTH_SIZE, 0,
            EGL_NONE
    };
    ..........
   return NO_ERROR;
}

然后再看threadloop 方法:如下代码在创建BootAnimation实例的时候,通过调用onFirstRef方法预加载了开机动画文件,并且将文件地址复制给mZipFileName,这里先看mZipFileName为空的时候,因为在编译源码的时候并没有设置开机动画bootanimation.zip所以mZipFileName肯定是空的。

scss 复制代码
bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {//判断开机动画文件是否为空 走默认的开机动画
        r = android();
    } else {
        r = movie();
    }
 }

下面看默认的开机动画是如何实现的 android()方法如下:

从代码中可以看出其实就会通过opengl进行绘制,do-while循环不断的绘制,chekExit方法检查是否退出开机动画

scss 复制代码
bool BootAnimation::android()
{
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
    mCallbacks->init({});

    ......

    const nsecs_t startTime = systemTime();
    do {
.............

        glDisable(GL_SCISSOR_TEST);
        glClear(GL_COLOR_BUFFER_BIT);

        glEnable(GL_SCISSOR_TEST);
        glDisable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);

        glEnable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
.  ........

        checkExit();
    } while (!exitPending());
    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}
退出开机动画

那么如何检查退出开机动画呢?checkExit方法如下:其实就是获取EXIT_PROP_NAME的属性值,如果为1就会退出

开机动画结束的标志是系统的首个 Activity(通常是 Launcher)进入空闲状态,即 Activity Idle 事件被触发时。这个事件会通知 ActivityManagerService,然后通过 WindowManagerService 终止开机动画

scss 复制代码
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
void BootAnimation::checkExit() {
    // Allow surface flinger to gracefully request shutdown
    char value[PROPERTY_VALUE_MAX];
    property_get(EXIT_PROP_NAME, value, "0");//EXIT_PROP_NAME 属性值为1的时候会进行退出
    int exitnow = atoi(value);
    if (exitnow) {
        requestExit();
        mCallbacks->shutdown();
    }
}

通过grep命令搜索,在frameworks目录中搜索更快,哪里设置了service.bootanim.exit这个属性值:

perl 复制代码
grep "service.bootanim.exit" ./ -rn

在StartPropertySetThread 属性service.bootanim.exit设置成了0 表示了开机动画启动

arduino 复制代码
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;
}

WindowsManagerServiceperformEnableScreen方法:service.bootanim.exit设置为了1,同时通过binder 跨进程通信调用了SurfaceFlinger中的方法执行关闭操作,同时在SurfaceFlinger 也设置了1,其实两次设置并没有特别的意义,wms只是设置了属性值为1,在sf中时调用了一系列的退出开机动画的操作

kotlin 复制代码
           if (!mBootAnimationStopped) {
                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
                SystemProperties.set("service.bootanim.exit", "1");
                mBootAnimationStopped = true;
                Log.d("lsm2","WindowManagerService performEnableScreen Stop bootanim",new Exception());
            }
..........
            try {
                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                if (surfaceFlinger != null) {
                    Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                    Parcel data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                            data, null, 0);
                    data.recycle();
                }
            } catch (RemoteException ex) {
                Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
            }

如何跨进程调用到sf进程呢?IBinder.FIRST_CALL_TRANSACTION,可以在ISurfaceComposer.h 看到这个赋值给了BOOT_FINISHED

kotlin 复制代码
class BnSurfaceComposer: public BnInterface<ISurfaceComposer>{
public:
    enum ISurfaceComposerTag {
      BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
      ........
    }
.........
}

ISurfaceComposer.cpp 中的onTransact 接受跨进程传递的信息:

kotlin 复制代码
status_t BnSurfaceComposer::onTransact(){
.....
 case BOOT_FINISHED: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            bootFinished();
            return NO_ERROR;
        }
......
}
class SurfaceFlinger : public BnSurfaceComposer,

SurfaceFlinger继承了BnSurfaceComposer,最终会调用到Surfaceflinger.cpp中的bootFinished

javascript 复制代码
void SurfaceFlinger::bootFinished()
{
    ........ 一系列的操作
    property_set("service.bootanim.exit", "1");
    .......
 }

那么整体的退出开机动画就清晰了,其实就是设置service.bootanim.exit 属性值为1就会退出开机动画,那么还有一个问题通过wms中的performEnableScreen方法才会设置这个属性值,如何触发的呢?可以加入java的日志调用栈分析Android 系统中调试追踪方法以Activity的启动流程为例

日志分析:

  1. 进入Activity第一次进入idle状态
  2. 跨进程调用到了AMS中,执行了postFinishBooting 方法
  3. 跨进程调用了WMS中的enableScreenAfterBoot方法
  4. 调用了performEnableScreen 设置停止开机动画属性值为1 ,并且跨进程调用了SurfaceFlingerbootFinished()方法
php 复制代码
//1. 进入idle
09-23 14:04:03.465  1602  2169 D lsm2    : Activity idle
09-23 14:04:03.465  1602  2169 D lsm2    : java.lang.Exception
09-23 14:04:03.465  1602  2169 D lsm2    :   at com.android.server.wm.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:1359)
09-23 14:04:03.465  1602  2169 D lsm2    :   at com.android.server.wm.ActivityTaskManagerService.activityIdle(ActivityTaskManagerService.java:1675)
09-23 14:04:03.465  1602  2169 D lsm2    :   at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1957)
09-23 14:04:03.465  1602  2169 D lsm2    :   at android.os.Binder.execTransactInternal(Binder.java:1021)
09-23 14:04:03.465  1602  2169 D lsm2    :   at android.os.Binder.execTransact(Binder.java:994)
//2. 跨进程调用到AMS中,执行了postFinishBooting
09-23 14:04:03.465  1602  1726 D lsm2    : postFinishBooting finishBooting true enableScreen true
09-23 14:04:03.465  1602  1726 D lsm2    : java.lang.Exception
09-23 14:04:03.465  1602  1726 D lsm2    :   at com.android.server.wm.ActivityTaskManagerService.lambda$postFinishBooting$6$ActivityTaskManagerService(ActivityTaskManagerService.java:5675)
09-23 14:04:03.465  1602  1726 D lsm2    :   at com.android.server.wm.-$$Lambda$ActivityTaskManagerService$oP6xxIfnD4kb4JN7aSJU073ULR4.run(Unknown Source:6)
09-23 14:04:03.465  1602  1726 D lsm2    :   at android.os.Handler.handleCallback(Handler.java:883)
09-23 14:04:03.465  1602  1726 D lsm2    :   at android.os.Handler.dispatchMessage(Handler.java:100)
09-23 14:04:03.465  1602  1726 D lsm2    :   at android.os.Looper.loop(Looper.java:214)
09-23 14:04:03.465  1602  1726 D lsm2    :   at android.os.HandlerThread.run(HandlerThread.java:67)
09-23 14:04:03.465  1602  1726 D lsm2    :   at com.android.server.ServiceThread.run(ServiceThread.java:44)
//3. 跨进程调用了WMS中的enableScreenAfterBoot 方法
09-23 14:04:03.473  1602  1726 D lsm2    : enableScreenAfterBoot -> performEnableScreen
09-23 14:04:03.473  1602  1726 D lsm2    : java.lang.Exception
09-23 14:04:03.473  1602  1726 D lsm2    :   at com.android.server.wm.WindowManagerService.enableScreenAfterBoot(WindowManagerService.java:3242)
09-23 14:04:03.473  1602  1726 D lsm2    :   at com.android.server.wm.ActivityTaskManagerService$LocalService.enableScreenAfterBoot(ActivityTaskManagerService.java:6485)
09-23 14:04:03.473  1602  1726 D lsm2    :   at com.android.server.wm.ActivityTaskManagerService.lambda$postFinishBooting$6$ActivityTaskManagerService(ActivityTaskManagerService.java:5680)
09-23 14:04:03.473  1602  1726 D lsm2    :   at com.android.server.wm.-$$Lambda$ActivityTaskManagerService$oP6xxIfnD4kb4JN7aSJU073ULR4.run(Unknown Source:6)
09-23 14:04:03.473  1602  1726 D lsm2    :   at android.os.Handler.handleCallback(Handler.java:883)
09-23 14:04:03.473  1602  1726 D lsm2    :   at android.os.Handler.dispatchMessage(Handler.java:100)
09-23 14:04:03.473  1602  1726 D lsm2    :   at android.os.Looper.loop(Looper.java:214)
09-23 14:04:03.473  1602  1726 D lsm2    :   at android.os.HandlerThread.run(HandlerThread.java:67)
09-23 14:04:03.473  1602  1726 D lsm2    :   at com.android.server.ServiceThread.run(ServiceThread.java:44)
//4. 调用了performEnableScreen 设置停止开机动画属性值为1,并且跨进程调用了SurfaceFlinger
09-23 14:04:04.248  1602  1726 D lsm2    : WindowManagerService performEnableScreen Stop bootanim
09-23 14:04:04.248  1602  1726 D lsm2    : java.lang.Exception
09-23 14:04:04.248  1602  1726 D lsm2    :   at com.android.server.wm.WindowManagerService.performEnableScreen(WindowManagerService.java:3322)
09-23 14:04:04.248  1602  1726 D lsm2    :   at com.android.server.wm.WindowManagerService.access$1100(WindowManagerService.java:288)
09-23 14:04:04.248  1602  1726 D lsm2    :   at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:4743)
09-23 14:04:04.248  1602  1726 D lsm2    :   at android.os.Handler.dispatchMessage(Handler.java:107)
09-23 14:04:04.248  1602  1726 D lsm2    :   at android.os.Looper.loop(Looper.java:214)
09-23 14:04:04.248  1602  1726 D lsm2    :   at android.os.HandlerThread.run(HandlerThread.java:67)
09-23 14:04:04.248  1602  1726 D lsm2    :   at com.android.server.ServiceThread.run(ServiceThread.java:44)

整个开机动画的完整流程如下图所示:

相关推荐
Dnelic-2 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen4 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年12 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿14 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神15 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛15 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法16 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter17 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快18 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl18 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5