前言
Android系統(tǒng)中最先被我們感知的就是Launcher界面,對于基于Android系統(tǒng)的智能設(shè)備來說,對Launcher進行定制是非常重要的一個環(huán)節(jié),那么了解Launcher的加載和啟動就顯得尤為重要,因此接下來會用幾章的篇幅進行分析闡述其中的原理。
Launcher啟動流程圖

對上述流程圖略作說明,當(dāng)按下啟動電源時,系統(tǒng)啟動后會加載引導(dǎo)程序,引導(dǎo)程序又會啟動 Linux 內(nèi)核,在加載 Linux 內(nèi)核時,它會在系統(tǒng)文件中尋找 init.rc 文件,并啟動 init 進程,之后會按著上圖中綠色部分依次啟動直至最后啟動 Launcher , 至此Android桌面展現(xiàn)。實際上Android系統(tǒng)的啟動流程要比上圖復(fù)雜的多,為了便于理解做了簡化,而接下來的幾篇會按著 init、Zygote、SystemService、ActivityManagerService、Launcher 這五個方面分別論述。
各進程間關(guān)系

由圖中可以看出,他們之間的通訊過程大致是:
- Launcher通過AIDL(Binder的一種)通知SystemService
- SystemService通過Socket通知Zygote
- Zygote 收到SystemService請求,fork自身生成應(yīng)用程序
- 應(yīng)用程序進程與SystemService通過Binder與AMS,WMS進行交互
Init 進程任務(wù)
- 創(chuàng)建和掛載啟動所需要的文件目錄
- 初始化和啟動屬性服務(wù)
- 解析 init.rc 配置文件并啟動 Zygote 進程
Init 源碼分析
為避免陷入到龐雜的源碼中,本小節(jié)會盡量的抽取 init 進程中的主要脈絡(luò)作為說明.
init 進程是 Android 啟動的第一個進程,進程號為 1 ,其源碼主要存在于 "/system/core/init" 目錄下。
(1) Init 進程入口
在 init 進程的入口函數(shù)為 main ,代碼如下所示:
int main(int argc, char** argv) {
...
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
umask(0);
clearenv();
// 創(chuàng)建和掛載啟動所需的文件目錄 注釋 ①
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
chmod("/proc/cmdline", 0440);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
mkdir("/mnt/vendor", 0755);
...
// 初始化 Kernel 的 Log,這樣就可以從外界獲取 Kernel 的日志 注釋②
InitKernelLogging(argv);
}
...
// 初始化屬性服務(wù)配置 注釋 ③
property_init();
...
// 用于設(shè)置紫禁城信號處理函數(shù),如果子進程(Zygote進程)異常退出,init 進程會調(diào)用該函數(shù)中
//設(shè)定的信號處理函數(shù)來進行處理 注釋 ④
sigchld_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
// 啟動屬性服務(wù) 注釋 ⑤
start_property_service();
set_usb_controller();
...
// 加載和解析 init.rc 文件 注釋⑥
LoadBootScripts(am, sm);
return 0;
}
(2) 屬性服務(wù)
Windows 平臺采用注冊表管理器來保存用戶、軟件的一些使用信息。這樣即使系統(tǒng)或者軟件重啟,就可以根據(jù)注冊表中的記錄,進行相應(yīng)的初始化工作。Android 提供類似的機制,叫做 屬性服務(wù) 。
在上述 main 函數(shù)中與屬性服務(wù)相關(guān)的代碼有兩行:
property_init();
start_property_service();
先來看看兩個函數(shù)的具體實現(xiàn):"/system/core/init/property_service.cpp"
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
}
__system_property_area_init 函數(shù)用來初始化屬性內(nèi)存區(qū)域,接下來看看 start_property_service 函數(shù)的具體實現(xiàn):
void start_property_service() {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
property_set("ro.property_service.version", "2");
// 創(chuàng)建非阻塞的 Socket 注釋 ①
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";
}
// 調(diào)用 listen 函數(shù)監(jiān)聽 property_set_fd 注釋 ②
listen(property_set_fd, 8);
// epoll 替換 select 處理大批量的文件描述符,handle_property_set_fd 用來處理客戶端的請求
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
可以看出在 注釋 ② 處調(diào)用 listen 函數(shù)對 property_set_fd 進行監(jiān)聽,這樣創(chuàng)建的 Socket 就成為 server,也就是 屬性服務(wù) ;listen 函數(shù)的第二個參數(shù)設(shè)置為 8 ,意味著屬性服務(wù)最多可以同時為8個試圖設(shè)置屬性的用戶提供服務(wù);
(3) 加載和解析 init.rc 文件
init 進程通過解析 init.rc 文件去啟動 Zygote 進程,這里通過 LoadBootScripts() 函數(shù)實現(xiàn):
/system/core/init/init.cpp:
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
如果沒有特殊配置ro.boot.init_rc,則解析./init.rc,把 /system/etc/init、/product/etc/init、/product_services/etc/init、/odm/etc/init、/vendor/etc/init 這幾個路徑加入init.rc之后解析的路徑,在init.rc解析完成后,解析這些目錄里的rc文件。這里需要說明一下,在 Android 7.0 之后,init.rc 進行了拆分,每個服務(wù)都有自己的 rc 文件,它們基本都被加載到這幾個目錄中,待解析之后,執(zhí)行相關(guān)的動作。關(guān)于 init.rc 文件的組成結(jié)構(gòu)可以參見 init.rc結(jié)構(gòu)說明