Android开机动画总共有三个过程。第一个开机动画是在Kenel启动时显示的,第二个开机动画是在init进程启动时显示的,这两个都是静态图片。第三个动画是在系统服务启动过程中显示的,他是一个动态图片,也是我们关注比较多的动画。
关于动画的播放,主要涉及frame buffer的知识,可以参考老罗的分析http://blog.csdn.net/luoshengyang/article/details/7691321/,从底层的角度,理清了Android的开机动画播放过程。
现在主要关注与开机动画相关的几个类、文件。

一、Kenel动画

Kenel logo是开机显示的第一个动画,用于显示Android内核正在启动过程中。播放的图片位于 vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo ,系统读取目录下的文件夹里面的logo图片(fwvga_kernel.bmp)显示到手机上的 。那么问题来了,系统默认读的是哪个文件夹? 如何客制化开机Kenel logo?我一修改这个文件夹不是所有的其他国家的logo都要变?

@1.1 系统默认使用哪个文件夹?
其实系统是有一套机制控制Kenel logo的,这也是客制化开机动画的基础,该目录下,有一个mk文件—rules.mk,他会决定当前系统使用哪一套开机kenel logo。

LOCAL_DIR := $(GET_LOCAL_DIR)BOOT_LOGO_DIR := $(LOCAL_DIR)#fix no boot_logo config#LOCAL_CFLAGS += -DBOOT_LOGO=wvgaifeq ($(strip $(BOOT_LOGO)),)  BOOT_LOGO = fwvgaendififeq ($(strip $(MTK_LK_CAMERA_SUPPORT)), yes)  BOOT_LOGO = fhdendif$(info BOOT_LOGO = $(BOOT_LOGO))$(info lk/logo/dir=$(LOCAL_DIR),builddir=$(BUILDDIR))

由上段代码可以看出来,在BOOT_LOGO没有赋值的情况下,默认使用fwvga文件夹下的Kenel logo。

@1.2 如何客制化开机Kenel logo?
看到这里,我们可以发现,如果要客制化开机Kenel logo图片,可以在rules.mk里面添加相关判断,以达到目的。以海外版本为例,我需要在发货海外的版本中,客制化kenel logo,就需要
1、在rules.mk中添加一下代码

#added by guohongcheng for kenel logo startifeq ($(strip $(KENEL_LOGO_PROC)), yes)  BOOT_LOGO = hd720Endif#added by guohongcheng for kenel logo end

这样当系统属性KENEL_LOGO_PROC是yes的时候,BOOT_LOGO会加载hd720下面的图片,

2、在ProjectConfig.mk中设置KENEL_LOGO_PROC值
而KENEL_LOGO_PROC这个属性值是在项目的ProjectConfig.mk设置的,添加一下语句

#added by guohongcheng for kenel logo startKENEL_LOGO_PROC=yes#added by guohongcheng for kenel logo end

3、配置完成过后我们只需在自己的自拟定文件夹hd720 中,( vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo 中将logo替换即可。
注意:
(1) 替换时所有图片的命名(不只是2张静态logo,其他的图片也需要拷贝到自拟定目录下进行改名)必须和默认文件夹保持一致 例如 xxxxxxxx_kernel.bmp 和 xxxxxxxx_uboot.bm
(2) 一般情况下要改61处文件名 这里给大家提供一个批量修改文件名的命令,防止人为造成的编译find -type f | grep cmcc| xargs rename ‘s/cmcc_lte_hd720/tl_lte_hd720_Italian/’参数为筛选选条件 第二个参数为需要替换的部分 第三个参数为替换成的部分

二、init 动画

@2.1 第二个开机动画的播放过程
第二个开机动画是在init进程开启时显示的图片,也就是在开启一系列系统进程的过程中显示。 init进程的入口函数main实现在文件system/core/init/init.cpp中,最终会调用console_init_action方法,代码如下:

static int console_init_action(const std::vector<std::string>& args){    std::string console = property_get("ro.boot.console");    if (!console.empty()) {        console_name = "/dev/" + console;    }    int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);    if (fd >= 0)        have_console = 1;#ifdef MTK_INIT    else        ERROR("console_init: can't open %s\n", console_name.c_str());#endif    close(fd);    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);    if (fd >= 0) {        const char *msg;            msg = "\n"        "\n"        "\n"        "\n"        "\n"        "\n"        "\n"  // console is 40 cols x 30 lines        "\n"        "\n"        "\n"        "\n"        "\n"        "\n"        "\n"        "             A N D R O I D ";        write(fd, msg, strlen(msg));        close(fd);    }

显示第二个开机画面是通过调用函数load_565rle_image来实现的。在调用函数load_565rle_image的时候,指定的开机画面文件为INIT_IMAGE_FILE。INIT_IMAGE_FILE是一个宏,定义在system/core/init/init.h文件中,如下所示:
#define INIT_IMAGE_FILE "/initlogo.rle" , 即第二个开机画面的内容是由文件/initlogo.rle来指定的。如果文件/initlogo.rle不存在,或者在显示它的过程中出现异常,那么函数load_565rle_image的返回值就会等于-1,这时候函数console_init_action就以文本的方式来显示第二个开机画面,即向编号为0的控制台(/dev/tty0)输出“ANDROID”这7个字符。

@ 2.2 init 动画客制化
init logo的客制化与kenel logo的客制化相同,他对应logo文件夹下的fwvga_uboot.bmp。

三、第三个开机动画

制作开关机动画
3.1 开机动画的位置
system/media/bootanimation.zip,要修改开机动画就是修改bootanimation这个压缩文件。如果不存在该压缩包,使用原生自带的资源,其路径在system/framework/framework-res.apk/assets/images(android-logo-mask.png,android-logo-shine.png),但是比较难看,比较常见的就是“android”。所以要定制自己的开关机动画一般都是在system/media/目录下放置bootanimation.zip和shutanimation.zip.这里以开机动画为例,关机动画和开机动画其原理一样。

3.2 bootanimation.zip文件结构

bootanimation里面主要包含一个desc.txt以及N个文件夹。而文件夹里面放着的就是开机动画的图片资源。decs.txt的作用就是指导系统如何去执行开机动画。
desc.txt编写规范,例如开机动画需要用到2个文件夹,分别是folder1和folder2,开机的时候,先把folder1里面的图片都播放一遍,然后再循环播放folder2里面的文件,直到进入系统,decs.txt文档的内容如下:

320 480 12  p 1 0 folder1  p 0 0 folder2  

320 480是代表屏幕的分辨率,12表示12帧每秒,简单地说12代表一秒钟播放12张图片;
p 1 0 part1:p就是play。1是播放一次,0是无限次。0代表阶段间隔帧数为0。folder1就是说,这条指令是针对folder1这个文件夹的;
p 0 0 part2:第一个0这里是代表循环播放,第二个0和上面第二条指令一样。folder2就是第二个文件夹。
总结规则如下:
第一条指令:[屏幕的分辨率] [播放频率]
第二条指令:[p] [播放次数] [间隔帧数] [文件夹]
第N条指令: 同上

3.3 压缩包
把需要用到的folder文件夹跟decs.txt打包成zip格式,必须是zip,不能是rar,且打包的时压缩方式选择“存储”模式。然后改名成为bootanimation.zip,最后将制作好的zip包push到/system/media目录下。
注意:bootanimation不能太大,一般最好不要超过3M。

@3.1 bootanimation的启动过程
第三个开机画面是由应用程序bootanimation来负责显示的。应用程序bootanimation在启动脚本system/core/rootdir/init.rc中被配置成了一个服务,如下所示:

service bootanim /system/bin/bootanimation    class core    user graphics    group graphics audio cw_access    disabled    oneshot

应用程序bootanimation的用户和用户组名称分别被设置为graphics。注意, 用来启动应用程序bootanimation的服务是disable的,即init进程在启动的时候,不会主动将应用程序bootanimation启动起来。当SurfaceFlinger服务启动的时候,它会通过修改系统属性ctl.start的值来通知init进程启动应用程序bootanimation,以便可以显示第三个开机画面,而当System进程将系统中的关键服务都启动起来之后,ActivityManagerService服务就会通知SurfaceFlinger服务来修改系统属性ctl.stop的值,以便可以通知init进程停止执行应用程序bootanimation,即停止显示第三个开机画面。接下来我们就分别分析第三个开机画面的显示过程和停止过程。
Zygote进程在启动的过程中,会将System进程启动起来,System进程在启动的过程中,会调用SurfaceFlinger类的静态成员函数instantiate来启动SurfaceFlinger服务。Sytem进程在启动SurfaceFlinger服务的过程中,首先会创建一个SurfaceFlinger实例,然后再将这个实例注册到Service Manager中去。在注册的过程,前面创建的SurfaceFlinger实例会被一个sp指针引用。从前面Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析一文可以知道,当一个对象第一次被智能指针引用的时候,这个对象的成员函数onFirstRef就会被调用。由于SurfaceFlinger重写了父类RefBase的成员函数onFirstRef,因此,在注册SurfaceFlinger服务的过程中,将会调用SurfaceFlinger类的成员函数onFirstRef。在调用的过程,就会创建一个线程来启动第三个开机画面。
SurfaceFlinger是在framework/native/services/surfaceflinger/Main_surfaceflinger.cpp 被启动的,首先会执行init方法,然后自行run方法,代码如下:

    // initialize before clients can connect    flinger->init();    // publish surface flinger    sp<IServiceManager> sm(defaultServiceManager());    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);    // publish GpuService    sp<GpuService> gpuservice = new GpuService();    sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);    // run surface flinger in this thread    flinger->run();在framework/native/services/surfaceflinger/SurfaceFlinger.cpp下,init()会方法的最后执行startBootAnim()void SurfaceFlinger::init() {    ALOGI(  "SurfaceFlinger's main thread ready to run. "            "Initializing graphics H/W...");        HAL_PIXEL_FORMAT_RGBA_8888);......    // set initial conditions (e.g. unblank default device)    initializeDisplays();    // start boot animation    startBootAnim();    ALOGV("Done initializing");}

startBootAnim()方法代码如下,它调用函数property_set来将系统属性“ctl.start”的值设置为“bootanim”,表示要将应用程序bootanimation启动起来,以便可以显示第三个开机画面。

void SurfaceFlinger::startBootAnim() {#ifdef MTK_AOSP_ENHANCEMENT    // dynamic disable/enable boot animation    checkEnableBootAnim();#else    // start boot animation    property_set("service.bootanim.exit", "0");    property_set("ctl.start", "bootanim");#endif}

当系统属性发生改变时,init进程就会接收到一个系统属性变化通知,这个通知最终是由在init进程中的函数handle_property_set_fd来处理的。 函数handle_property_set_fd实现在文件system/core/init/property_service.c中,如下所示:

static void handle_property_set_fd(){    prop_msg msg;    int s;    int r;    struct ucred cr;    struct sockaddr_un addr;    socklen_t addr_size = sizeof(addr);    socklen_t cr_size = sizeof(cr);    char * source_ctx = NULL;    struct pollfd ufds[1];    const int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */    int nr;    ......    switch(msg.cmd) {    case PROP_MSG_SETPROP:        msg.name[PROP_NAME_MAX-1] = 0;        msg.value[PROP_VALUE_MAX-1] = 0;        if (!is_legal_property_name(msg.name, strlen(msg.name))) {            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);            close(s);            return;        }        getpeercon(s, &source_ctx);        if(memcmp(msg.name,"ctl.",4) == 0) {            // Keep the old close-socket-early behavior when handling            // ctl.* properties.            close(s);            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {#ifdef MTK_INIT                INFO("[PropSet]: pid:%u uid:%u gid:%u %s %s\n", cr.pid, cr.uid, cr.gid, msg.name, msg.value);#endif                handle_control_message((char*) msg.name + 4, (char*) msg.value);            } else {                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);            }        } else {......    }}

更多相关文章

  1. Android应用进程分裂分析
  2. 【腾讯Bugly干货分享】Android进程保活招式大全
  3. 理解Android系统的进程间通信原理(二)----RPC机制
  4. 框架层理解Activity生命周期(APP启动过程)
  5. Android性能测试工具APT使用指南
  6. Android中app进程ABI确定过程
  7. 关于Android(安卓)studio中httpclient不能用的问题
  8. 通信之进程间通信-AIDL
  9. android aidl 使用实例

随机推荐

  1. android 入门 ListView - ArrayAdapter、
  2. android 控件各种颜色的半透明效果配置
  3. Android 获取设置屏幕横竖屏
  4. android在xml文件中实现背景或按钮颜色渐
  5. android 音量键调节无效问题
  6. Android(安卓)UI界面目前做的最好的一个:
  7. Android -- 跨应用启动Service
  8. Android中的Handler通信机制源码解析
  9. Android之Tinker使用
  10. android studio 中去除应用标题栏