Android Framework学习教程-BootAnimation开机动画启动流程源码分析

Posted learnframework

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Framework学习教程-BootAnimation开机动画启动流程源码分析相关的知识,希望对你有一定的参考价值。

课程答疑和新课信息:QQ交流群:422901085进行课程讨论
FrameWork入门课视频链接:https://edu.csdn.net/course/detail/30298
FrameWork实战课1视频链接:https://edu.csdn.net/course/detail/30275
专题博客系列:
Android 8.1 zygote 启动过程源码
Android Framework实战视频–Zygote的fork进程篇
Android Framework实战视频–SystemServer启动篇
Android Framework实战视频–SystemServer启动FallbackHome篇
Android Framework实战视频–FallbackHome进程启动及Activity启动篇
Android Framework实战视频–FallbackHome结束启动Launcher篇
Android Framework实战视频–BootAnimation的启动源码分析(Android8.1)

Android Framework实战视频–init进程的bootanimation启动源码分析(补充Android 10部分的BootAnimation的启动源码分析)

课前问题:

请问大家在android系统中要展示一个界面,不使用View方式还有没有其他方式?
.1 Android开机动画实现方式目前实现Android开机动画的方式主要是逐帧动画和OpenGL直接编程绘制动画。

1、逐帧动画

逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。 因为逐帧动画的帧序列内容不一样,不但给制作增加了负担而且最终输出的文件量也很大,但它的优势也很明显:逐帧动画具有非常大的灵活性,几乎可以表现任何想表现的内容,而它类似与电影的播放模式,很适合于表演细腻的动画。
逐帧动画是广泛流传的一种实现方法。实现原理是将一系列图片打包成bootanimation.zip放入/system/media/目录,系统将图片一帧一帧循环播放形成一个动画效果。理论上讲这种方法应该是可以实现一切动画需求的,但是实践后你会发现当bootanimation.zip大于5M的时候,动画将有明显卡顿,文件越大动画越不流畅。所以细心的同学会发现手机上的开机动画大多都是只有中间一小部分在变化,而四周全是黑色,这样做是为了使得可以采用100*50(甚至更小)分辨率的图片,这样100帧也才几M的大小。

2、OpenGL动画

OpenGL(英语:Open Graphics Library)是个定义了一个跨编程语言、跨平台的应用程序接口(API)的规范,它用于生成二维、三维图像。这个接口由近三百五十个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。

代码分析路径介绍:

bootanimation   frameworks/base/cmds/bootanimation/
surfaceflinger  frameworks/native/services/surfaceflinger/
init            system/core/init/

启动流程详细分析:
内核起来后会启动第一个进程,即init进程。

init进程会根据init.rc配置启动surfaceflinger进程。

service surfaceflinger /system/bin/surfaceflinger
    class main
    user system
    group graphics drmrpc
    onrestart restart zygote

surfaceflinger进程便启动了,跟着就会跑进程的main()函数。

frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

int main(int argc, char** argv) {
....
 
    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();//创建surfaceflinger服务
 
....
    flinger->init();
 
    // publish surface flinger
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);//注册到servicemanager里
 
    // run in this thread
    flinger->run();//运行
 
    return 0;
}

首先new一个SurfaceFlinger实例,然后init,然后run

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");


    // Inform native graphics APIs whether the present timestamp is supported:
    if (getHwComposer().hasCapability(
            HWC2::Capability::PresentFenceIsNotReliable)) {
        mStartPropertySetThread = new StartPropertySetThread(false);
    } else {
        mStartPropertySetThread = new StartPropertySetThread(true);
    }

    if (mStartPropertySetThread->Start() != NO_ERROR) { //真正启动设置bootanimation的属性线程
        ALOGE("Run StartPropertySetThread failed!");
    }

    ALOGV("Done initializing");
}

初始化graphics之后,mStartPropertySetThread()播放开机动画。//注意已经不是以前的startBootAnim方法

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;
}

这样bootanim进程就会启动?凭什么设置了一个属性就启动了?那么下面我们来看,/system/core/init/init.cpp ,在看init进程的init.cpp的main函数中:

int main(int argc, char** argv) {
//省略
    property_load_boot_defaults();
    export_oem_lock_status();
    start_property_service(); //start_property_service
    set_usb_controller();
}

下面来来看看start_property_service方法,在/system/core/init/property_service.cpp:
main函数中start_property_service(),在这个函数中注册一个epoll handle 的机制

register_epoll_handler()void start_property_service() {
    property_set("ro.property_service.version", "2");
    
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,0666, 0, 0, NULL);
    if (property_set_fd == -1) {
    PLOG(ERROR) << "start_property_service socket creation failed";
    exit(1);
    }
    
    listen(property_set_fd, 8);
    
    register_epoll_handler(property_set_fd, handle_property_set_fd);
    }

init进程会使用epoll机制来轮询事件,其中一个事件是系统属性值被修改。得到该事件后,会执行handle_property_set_fd(),代码如下:通过handle_property_set_fd():

static void handle_property_set_fd() {
  //省略
     switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];

if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
return;
          }

prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;

handle_property_set(socket, prop_value, prop_value, true);
break;
}

该函数会进执行handle_property_set()

static void handle_property_set(SocketConnection& socket,
                                const std::string& name,
                                const std::string& value,
                                bool legacy_protocol) {
 //省略。。。。。。
      handle_control_message(name.c_str() + 4, value.c_str());
 //省略。。。。。。。
}
该函数会进一步执行handle_control_message(),在/system/core/init/init.cpp,传入的参数msg.name=ctl.start,
msg.value=bootanim

    void handle_control_message(const std::string& msg, const std::string& name) {
    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
    if (svc == nullptr) {
    LOG(ERROR) << "no such service '" << name << "'";
    return;
    }
    
    if (msg == "start") {
    svc->Start();
    } else if (msg == "stop") {
    svc->Stop();
    } else if (msg == "restart") {
    svc->Restart();
    } else {
    LOG(ERROR) << "unknown control msg '" << msg << "'";
    }
    }

该函数首先调用FindServiceByName,从service_list中查询要启动的服务是否有存在,若存在,返回服务的相关信息。因为init.rc中有bootanimation的定义,因此在init进程执行parse_config()时,会将该服务添加到service_list中,所以bootanimation应用是存在的。然后,如果找到了该服务,就调用service_start启动服务。

把service.bootanim.exit属性设为0,这个属性bootanimation进程里会周期检查,=1时就退出动画,这里=0表示要播放动画。
后面通过ctl.start的命令启动bootanimation进程,动画就开始播放了。

下面来到bootanimation的实现

frameworks/base/cmds/bootanimation/bootanimation_main.cpp

int main(int argc, char** argv)
{
 
 
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();
 
        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation();//创建BootAnimation实例
 
        IPCThreadState::self()->joinThreadPool();//binder线程池,与surfaceflinger通信用的。
 
    }
    return 0;
}

以上是关于Android Framework学习教程-BootAnimation开机动画启动流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android大厂面试必问的Android Framework 如何学习,如何深入了解Framework层?

学习Android Framework可以做什么?

学习Android Framework可以做什么?

Android Framework学习教程-BootAnimation开机动画启动流程源码分析

Android Framework 如何学习,如何从应用深入到Framework?

Android Framework 如何学习,如何从应用深入到Framework?