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)

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

相关推荐
ITPlus1 小时前
解决新版Android studio不能连接手机的问题
android·智能手机·android studio
赵庆明老师2 小时前
【原创】可用于 Android Studio 的翻译插件
android·ide·android studio
LMF·2 小时前
Android Studio 打包混淆失效问题
android·ide·android studio
赵庆明老师2 小时前
【原创】MacOS 上Android Studio 登录问题解决方法
android·macos·android studio
读心悦2 小时前
MySQL 多条件查询
android·数据库·mysql
Dymc3 小时前
【安装JDK和Android SDK】
android·java·jdk·sdk
赛恩斯4 小时前
安卓上的iso 是哪几个gain 相乘
android·人工智能·计算机视觉
深海呐4 小时前
Android Compose 控件基本属性
android
东坡大表哥4 小时前
Android设置边框圆角
android
齐适杨5 小时前
Databinding(kotlin)
android·开发语言·kotlin