安卓系统学习笔记(一)

    提到 Android 系统的 UI ,我们最先接触到的便是系统在启动过程中所出现的画面了。 Android 系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段。本文将详细分析这三个开机画面的显示过程,以便可以开启我们对 Android 系统 UI 实现的分析之路。

    第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。第三个开机画面是在系统服务启动的过程中出现的,它是一个动态的画面。无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。接下来,我们就分别分析这三个画面是如何在fb上显示的。

1. 第一个开机画面的显示过程

        Android系统的第一个开机画面其实是Linux内核的启动画面。在默认情况下,这个画面是不会出现的,除非我们在编译内核的时候,启用以下两个编译选项:

        CONFIG_FRAMEBUFFER_CONSOLE

        CONFIG_LOGO

        第一个编译选项表示内核支持帧缓冲区控制台,它对应的配置菜单项为:Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support。第二个编译选项表示内核在启动的过程中,需要显示LOGO,它对应的配置菜单项为:Device Drivers ---> Graphics support ---> Bootup logo。配置Android内核编译选项可以参考在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)一文。

2. 第二个开机画面的显示过程

      由于第二个开机画面是在init进程启动的过程中显示的,因此,我们就从init进程的入口函数main开始分析第二个开机画面的显示过程。

  init进程的入口函数main实现在文件system/core/init/init.c中,如下所示:
  1. int main(int argc, char **argv)  
  2. {  
  3.     int fd_count = 0;  
  4.     struct pollfd ufds[4];  
  5.     ......  
  6.     int property_set_fd_init = 0;  
  7.     int signal_fd_init = 0;  
  8.     int keychord_fd_init = 0;  
  9.   
  10.     if (!strcmp(basename(argv[0]), "ueventd"))  
  11.         return ueventd_main(argc, argv);  
  12.   
  13.     ......  
  14.   
  15.     queue_builtin_action(console_init_action, "console_init");  
  16.   
  17.     ......  
  18.   
  19.     for(;;) {  
  20.         int nr, i, timeout = -1;  
  21.   
  22.         execute_one_command();  
  23.         restart_processes();  
  24.   
  25.         if (!property_set_fd_init && get_property_set_fd() > 0) {  
  26.             ufds[fd_count].fd = get_property_set_fd();  
  27.             ufds[fd_count].events = POLLIN;  
  28.             ufds[fd_count].revents = 0;  
  29.             fd_count++;  
  30.             property_set_fd_init = 1;  
  31.         }  
  32.         if (!signal_fd_init && get_signal_fd() > 0) {  
  33.             ufds[fd_count].fd = get_signal_fd();  
  34.             ufds[fd_count].events = POLLIN;  
  35.             ufds[fd_count].revents = 0;  
  36.             fd_count++;  
  37.             signal_fd_init = 1;  
  38.         }  
  39.         if (!keychord_fd_init && get_keychord_fd() > 0) {  
  40.             ufds[fd_count].fd = get_keychord_fd();  
  41.             ufds[fd_count].events = POLLIN;  
  42.             ufds[fd_count].revents = 0;  
  43.             fd_count++;  
  44.             keychord_fd_init = 1;  
  45.         }  
  46.   
  47.         if (process_needs_restart) {  
  48.             timeout = (process_needs_restart - gettime()) * 1000;  
  49.             if (timeout < 0)  
  50.                 timeout = 0;  
  51.         }  
  52.   
  53.         if (!action_queue_empty() || cur_action)  
  54.             timeout = 0;  
  55.   
  56.         ......  
  57.   
  58.         nr = poll(ufds, fd_count, timeout);  
  59.         if (nr <= 0)  
  60.             continue;  
  61.   
  62.         for (i = 0; i < fd_count; i++) {  
  63.             if (ufds[i].revents == POLLIN) {  
  64.                 if (ufds[i].fd == get_property_set_fd())  
  65.                     handle_property_set_fd();  
  66.                 else if (ufds[i].fd == get_keychord_fd())  
  67.                     handle_keychord();  
  68.                 else if (ufds[i].fd == get_signal_fd())  
  69.                     handle_signal();  
  70.             }  
  71.         }  
  72.     }  
  73.   
  74.     return 0;  
  75. }  
         函数一开始就首先判断参数argv[0]的值是否等于“ueventd”,即当前正在启动的进程名称是否等于“ueventd”。如果是的话,那么就以ueventd_main函数来作入口函数。这是怎么回事呢?当前正在启动的进程不是init吗?它的名称怎么可能会等于“ueventd”?原来,在目标设备上,可执行文件/sbin/ueventd是可执行文件/init的一个符号链接文件,即应用程序ueventd和init运行的是同一个可执行文件。内核启动完成之后,可执行文件/init首先会被执行,即init进程会首先被启动。init进程在启动的过程中,会对启动脚本/init.rc进行解析。在启动脚本/init.rc中,配置了一个ueventd进程,它对应的可执行文件为/sbin/ueventd,即ueventd进程加载的可执行文件也为/init。因此,通过判断参数argv[0]的值,就可以知道当前正在启动的是init进程还是ueventd进程。

        ueventd进程是作什么用的呢?它是用来处理uevent事件的,即用来管理系统设备的。从前面的描述可以知道,它真正的入口函数为ueventd_main,实现在system/core/init/ueventd.c中。ueventd进程会通过一个socket接口来和内核通信,以便可以监控系统设备事件。例如,在前面在Ubuntu上为Android系统编写Linux内核驱动程序一文中, 我们调用device_create函数来创建了一个名称为“hello”的字符设备,这时候内核就会向前面提到的socket发送一个设备增加事件。ueventd进程通过这个socket获得了这个设备增加事件之后,就会/dev目录下创建一个名称为“hello”的设备文件。这样用户空间的应用程序就可以通过设备文件/dev/hello来和驱动程序hello进行通信了。

         函数一开始就首先判断参数argv[0]的值是否等于“ueventd”,即当前正在启动的进程名称是否等于“ueventd”。如果是的话,那么就以ueventd_main函数来作入口函数。这是怎么回事呢?当前正在启动的进程不是init吗?它的名称怎么可能会等于“ueventd”?原来,在目标设备上,可执行文件/sbin/ueventd是可执行文件/init的一个符号链接文件,即应用程序ueventd和init运行的是同一个可执行文件。内核启动完成之后,可执行文件/init首先会被执行,即init进程会首先被启动。init进程在启动的过程中,会对启动脚本/init.rc进行解析。在启动脚本/init.rc中,配置了一个ueventd进程,它对应的可执行文件为/sbin/ueventd,即ueventd进程加载的可执行文件也为/init。因此,通过判断参数argv[0]的值,就可以知道当前正在启动的是init进程还是ueventd进程。

        ueventd进程是作什么用的呢?它是用来处理uevent事件的,即用来管理系统设备的。从前面的描述可以知道,它真正的入口函数为ueventd_main,实现在system/core/init/ueventd.c中。ueventd进程会通过一个socket接口来和内核通信,以便可以监控系统设备事件。例如,在前面在Ubuntu上为Android系统编写Linux内核驱动程序一文中, 我们调用device_create函数来创建了一个名称为“hello”的字符设备,这时候内核就会向前面提到的socket发送一个设备增加事件。ueventd进程通过这个socket获得了这个设备增加事件之后,就会/dev目录下创建一个名称为“hello”的设备文件。这样用户空间的应用程序就可以通过设备文件/dev/hello来和驱动程序hello进行通信了。

3. 第三个开机画面的显示过程

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

  1. service bootanim /system/bin/bootanimation  
  2.     user graphics  
  3.     group graphics  
  4.     disabled  
  5.     oneshot 

文章摘选自老罗:http://blog.csdn.net/luoshengyang/article/details/7691321

       


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值