系列文章:
前言
上文Android Activity生命周期,啟動模式,啟動過程詳解我們講解了Activity的生命周期,以及Activity啟動過程的詳細步驟,Activity承載著App對用戶的內容展示,當我們在手機桌面上點開一個圖標便打開了一個App,App上顯示的界面對應背后的Activity,那么為什么點擊圖標便能打開App呢,其實桌面應用程序(Launcher)我們也可以理解為一個App,此Activity上承載著各種App圖標,點擊圖標便有一個響應事件,這個響應事件就是打開圖標對應的App。那么Launcher程序又是如何啟動的呢?自然是和手機開機有關,因為我們知道手機開機后便是默認桌面,所以本文我們將從手機開機說起,梳理一下從手機開機,到啟動Launcher程序,到點擊Icon打開APP這個過程,下面分別介紹以上三個過程。
本文源碼基于Android-25版本
開機過程分析
在看Android系統啟動流程前,我們先了解下計算機PC啟動過程,大概分為以為四步:
- BIOS:主要做硬件自檢,轉移控制權等工作;
- 主引導記錄:BIOS把控制權轉交給排在第一位的儲存設備(主引導記錄),共512字節;
- 硬盤啟動:計算機的控制權由硬盤的某個分區控制;
- 操作系統:計算機的控制權轉交給操作系統,內核首先被載入內存,以Linux為例,其第一個運行的程序是/sbin/init。它根據配置文件產生init進程,init是Linux啟動后的第一個進程,pid進程編號為1,其他進程都是它的后代。然后,init線程加載系統的各個模塊,比如窗口程序和網絡程序,直至執行/bin/login程序,跳出登錄界面,等待用戶輸入用戶名和密碼。
以上摘自 計算機是如何啟動的?[阮一峰]
因為Android系統是基于Linux內核的,所以系統的開機過程肯定涉及到Linux內核的啟動。但是Android系統屬于嵌入式系統,沒有計算機那樣的BIOS引導程序,取而代之的是系統引導Bootloader,為啟動系統內核做好準備。Android中也沒有硬盤,取而代之的是ROM,類似硬盤存放操作系統和應用程序等。整體開機流程如下圖所示:
第一步:上電復位
開機時,通電產生一個CPU復位信號,CPU開始執行指令,第一條指令是設定好的(固化在ROM上),將引導程序(Bootloader)加載到RAM中;
第二步:Bootloader
Bootloader啟動,引導進入Linux內核;引導程序是運行的第一個程序,不同的主板和芯片具有不同引導程序,不同的手機廠商使用不同的程序,這部分不屬于Android操作系統,因此可能是廠商加鎖的地方;
第三步:Linux內核
Linux內核啟動后主要做一些初始化工作,比如初始化軟硬件環境,加載驅動程序,掛載根文件系統,內核加載的最后階段啟動第一個進程init進程;
第四步:init進程
init進程(/system/core/init/*)是系統第一個進程,進程號為1,該進程會首先加載一個init.rc配置文件,init.rc文件是Android系統的重要配置文件,位于(/system/core/rootdir/init.rc),其主要功能是定義了系統啟動時需要執行的一系列動作,設置環境變量,生成系統運行所需要的文件或目錄,執行特定Services等。
Android針對init.rc有特定的格式和規則,它由Android Init Language語言編寫而成,Android Init Language主要包含四種聲明:Actions(動作),Commands(命令),Services(服務),Options(選項)。
通過init.rc腳本主要啟動了以下服務:
Action/Service | 描述 |
---|---|
on early-init | 設置init進程以及它創建的子進程的優先級,設置init進程的安全環境 |
on init | 設置全局環境,為cpu accounting創建cgroup(資源控制)掛載點 |
on fs | 掛載mtd分區 |
on post-fs | 改變系統目錄的訪問權限 |
on post-fs-data | 改變/data目錄以及它的子目錄的訪問權限 |
on boot | 基本網絡的初始化,內存管理等等 |
service servicemanager | 啟動系統管理器管理所有的本地服務,比如位置、音頻、Shared preference等等… |
service zygote | 啟動zygote作為應用進程 |
本表來自Android啟動過程深入解析
init.rc最重要的的任務是啟動一個Zygote(孵化器)進程,此進程負責啟動Android應用進程的啟動工作。init.rc通過include引入init.zygote.rc創建Zygote進程。
init.rc的具體語法可以參考深入分析AIL語言及init.rc文件
第五步:Zygote進程
Zygote進程孵化了所有Android應用進程,是Android Framework的基礎。在Java中,不同的虛擬機實例會為不同的應用分配不同的內存。Android應用程序是運行在Dalvik虛擬機里面的,并且每一個應用程序對應有一個單獨的Dalvik虛擬機實例。但是Android應用程序中的Dalvik虛擬機實例實際上是從Zygote進程的地址空間拷貝而來的,這樣就可以加快Android應用程序的啟動速度,Zygote讓Dalvik虛擬機共享代碼,低內存占用,最小啟動時間成為可能。
Zygote其實是一個虛擬機進程,它會完成虛擬機的初始化、庫的加載、預制類庫、核心類庫的初始化等,當系統需要一個新的虛擬機時,它會迅速復制自己,提供給系統。每當我們打開一個新的APP時都會fork之前的Zygote進程。下面介紹下Zygote啟動過程:
App_main.main()
此函數位于(frameworks/base/cmds/app_process/App_main.cpp)中,主要添加了Android運行環境,即創建一個AppRuntime變量runtime,而AppRuntime繼承自AndroidRuntime,在App_main.main()方法中執行了runtime.start()方法,也就是執行了AndroidRuntime類的start方法,由于在init.rc文件中設置了啟動app_process的參數為:--zygote
和--start-system-server
,因此實際上在main函數里最終會執行下面語句:
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
AndroidRuntime.start()
此函數位于(frameworks/base/core/jni/AndroidRuntime.cpp)中,主要做了以下三件事情:
- startVm():創建虛擬機,主要是關于虛擬機參數的設置;
- startReg(): 注冊JNI方法;
- env->GetStaticMethodID(): 通過JNI調用Java函數,進入Java代碼中,實際上最終調用了com.android.internal.os.ZygoteInit的main函數。
ZygoteInit.main()
此函數位于(frameworks/base/core/java/com/android/internal/os/ZygoteInit.java)中:
public static void main(String argv[]) {
try {
......
String socketName = "zygote";
......
registerZygoteSocket(socketName);
preload();
......
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
......
} catch (RuntimeException ex) {
......
}
}
主要做了以下幾件事情:
- registerZygoteSocket():創建一個名為Zygote的socket接口,用來和ActivityManagerService通訊
- preload():預加載通用類等資源,如res(drawable,xml信息,strings)等
- startSystemServer(): 啟動SystemServer等服務,在此函數內部Zygote進程通過Zygote.forkSystemServer函數來創建一個新的進程來啟動SystemServer組件,如下所示:
private static boolean startSystemServer(){
....
// SystemServer是由Zygote通過Zygote.forkSystemServer函數fork出來的
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
....
// 子進程返回0,即SystemServer
if (pid == 0) {
....
handleSystemServerProcess(parsedArgs);
}
}
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
// 關閉這里繼承來的socket,因為這里的子進程并不會使用到socket
closeServerSocket();
// 繼續啟動SystemServer的操作
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
/* should never reach here */
}
public static final void zygoteInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
....
// 初始化Binder進程間通信機制
zygoteInitNative();
....
}
- runSelectLoopMode(): 輪詢監聽socket,不斷處理來自客戶端的AMS請求,然后交給runOnce()處理
至此,Zygote進程就啟動完成了,總結如下:
- init進程創建Zygote進程,而Zygote負責整個后續Android 應用程序的創建
- Zygote進程啟動時會創建SystemServer進程,SystemServer進程負責啟動系統的關鍵服務,如ActivityManagerService,PowerManagerService,PackageManagerService等
- 當我們準備啟動一個APP時,AMS通過Socket和Zygote進程間通信,通知Zygote fork子進程,加載需要的類。
整體過程如下圖所示:
SystemServer啟動過程
上述啟動Zygote進程的過程中,我們提到了啟動SystemServer這個Android系統核心進程,為了更清楚理解Android系統工作,這里我們再將啟動SystemServer進程的過程單獨列為一小節說明。
SystemServer由Zygote fork而成,進程名為system_server,承載著framework服務。
具體的SystemServer啟動過程較為復雜,通過一系列方法最終轉移到SystemServer類的main()方法中,如下圖所示:
圖片來自 作者:Gityuan博客 來源:Android系統啟動-SystemServer上篇
鏈接:http://gityuan.com/2016/02/14/android-system-server/
SystemServer.main()方法主要的流程如下:
SystemServer.main() // 初始化SystemServer對象,再調用run()方法
SystemServer.run() // 調用以下方法
createSystemContext() // 創建system_server進程的上下文信息
startBootstrapServices(); // 創建AMS,PMS,LightService,DisplayManagerService等服務
startCoreServices(); // 啟動BatteryService,UsageStatsService,WebViewUpdateService服務
startOtherServices(); // 顯示啟動界面,調用AMS.systemReady()方法
Looper.loop(); // 開啟消息循環
在上面的startOtherServices()方法中有一段代碼如下:
mActivityManagerService.systemReady(new Runnable() {
public void run() {
...
}
});
public final class ActivityManagerService{
public void systemReady(final Runnable goingCallback) {
....
startHomeActivityLocked(mCurrentUserId, "systemReady"); //啟動桌面
mStackSupervisor.resumeTopActivitiesLocked(); //恢復棧頂的Activity
}
}
AMS的systemReady()方法中啟動了WebView,SystemUI,開啟WatchDog,啟動桌面Launcher 程序。
啟動Launcher的過程如下:首先通過Zygote進程fork一個子進程作為APP進程,然后創建Application,再啟動Activity,最后顯示出實際畫面。
完整的啟動過程如下:
本圖片摘自 作者: Jeanboydev 來源:CSDN
鏈接 https://blog.csdn.net/freekiteyu/article/details/79318031
Launcher
從上面分析我們知道,AMS在完成服務注冊等初始化工作后,在main()方法中調用systemReady()方法,進而調用startHomeActivityLocked()來啟動Launcher。
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
// 獲取啟動Launcher的intent信息
Intent intent = getHomeIntent();
// 通過PackageManagerService獲得Launcher的Activity描述信息Info
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
啟動Launcher的Intent對象中添加了Intent.CATEGORY_HOME常量,代表將要啟動Home界面。
獲得intent和ActivityInfo后,后續方法調用順序為:
ActivityStarter.startHomeActivityLocked()
ActivityStackSupervisor.moveHomeStackTaskToTop() // 把Launcher的堆棧移到頂部
ActivityStarter.startActivityLocked()
......
進入ActivityStarter.startActivityLocked()方法后,后續過程就和啟動一個普通的Activity沒什么區別,啟動一個普通Activity的過程可以參考我的上一篇文章Android Activity生命周期,啟動模式,啟動過程詳解。
第一次啟動Launcher時,執行到ActivityStackSupervisor的startSpecificActivityLocked()方法時,會判斷Launcher Activity所在進程是否已經存在,如果不存在則會創建一個進程容納Activity,創建進程的流程就是通過AMS向Zygote發起請求,Zygote收到請求后fork一個子進程,然后再繼續啟動Launcher。創建子進程的過程,我們會在下文中點擊Icon啟動一個APP的過程中進行分析。
總而言之,Launcher的啟動過程就是類似一個啟動Activity的過程。但是,我們知道Launcher上放著各種應用程序圖標,有著不同的狀態,因此其Activity實現具體原理還可以進一步探究。可以參考Android Launcher加載流程源碼分析 、 Android M Launcher3主流程源碼淺析、Android系統啟動流程(四)Launcher啟動過程與系統啟動流程
應用安裝的時候,通過PackageManagerService解析apk的AndroidManifest.xml文件,提取出這個apk的信息寫入packages.xml中,包括權限、包名、icon、apk安裝位置、版本、userID等信息,Launcher為已安裝的程序在桌面上生成圖標,點擊圖標便可啟動應用,下面分析啟動應用的過程。
本段來自 Jeanboydev CSDN博客 一篇文章看明白 Android 從點擊應用圖標到界面顯示的過程
點擊Icon啟動APP
其實點擊Icon啟動APP的過程就是啟動APP主Activity的過程,即啟動MainActivity的過程,從而把APP啟動起來,但是當我們新啟動一個APP過程時,需要判斷此APP所在進程是否已經存在,如果不存在則需要fork一個子進程。下面詳細介紹這個具體過程。
Launcher.startActivitySafely
點擊圖標時,桌面程序Launcher.java做出響應,調用Launcher.startActivitySafely來啟動一個Activity
public class Launcher extends Activity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
AccessibilityManager.AccessibilityStateChangeListener {
......
public void onClick(View v) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v);
}
......
}
protected void onClickAppShortcut(final View v) {
......
startAppShortcutOrInfoActivity(v);
}
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent = item.getIntent();
......
boolean success = startActivitySafely(v, intent, item);
......
}
public boolean startActivitySafely(Intent intent, Object tag) {
......
// 表示在新的Task中啟動此Activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
......
try {
......
startActivity(intent);
} catch (ActivityNotFoundException e) {
......
} catch (SecurityException e) {
......
}
}
......
}
Activity.startActivity
Launcher繼承自Activity,因此上段代碼中startActivity(intent)便調用了Activity類的該方法:
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
Activity的StartActivity()方法最終調用了startActivityForResult()方法,這和我之前一篇文章Android Activity生命周期,啟動模式,啟動過程詳解相同,后續過程基本沒有區別。和前文類似,當執行到ActivityStackSupervisor的startSpecificActivityLocked()方法時,會判斷APP所在進程是否已經存在,如果不存在則會fork一個進程。下面介紹fork新進程的過程:
fork新進程
Android創建進程的流程圖大概如下:
圖片來自 作者:Gityuan博客 來源:理解Android進程創建流程
鏈接:http://gityuan.com/2016/03/26/app-process-create/
ActivityManagerService.startProcessLocked()
ActivityStackSupervisor的startSpecificActivityLocked()的方法中有下面一段代碼
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
第一次啟動APP時,很明顯上面獲得的變量app=null,在配置文件AndroidManifest.xml中我們如果沒有指定process屬性,系統會默認使用package的名稱當做process名。而且每一個程序都有自己的uid,uid+process的組合就可以為每一個應用程序創建一個ProcessRecord,每次新建進程前都會判斷此ProcessRecord是否存在,如果已經存在則不會新建進程。
如果不存在,則新建進程:
mService.startProcessLocked(r.processName, r.info.applicationInfotrue, 0,
"activity", r.intent.getComponent(), false, false, true);
因為startProcessLocked有多個重載形式,上述方法最終調用的方法為:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
......
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
......
}
最終轉到Process的start()方法中。
Process.start()
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
......
}
}
又繼續調用Process.startViaZygote()方法:
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
......
// openZygoteSocketIfNeeded(abi)方法是根據當前的abi來選擇與zygote還是zygote64來進行通信
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
繼續調用zygoteSendArgsAndGetResult()方法:
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
......
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
ProcessStartResult result = new ProcessStartResult();
// 等待Zygote的socket返回新進程的pid
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
通過socket向Zygote發送一系列參數,然后進入阻塞等待狀態,直到遠端的socket服務端發送回來新創建的進程pid才返回。Zygote收到請求后,開始工作,我們又回到了ZygoteInit.main()方法中。
ZygoteInit.main()
ZygoteInit.main()方法中主要停留在runSelectLoop函數中,等待AMS的請求,收到請求時會調用ZygoteConnection的runOnce函數來處理請求,后序函數調用邏輯為:
ZygoteConnection.runOnce()
Zygote.forkAndSpecialize() // fork當前進程來創建一個子進程
ZygoteConnection.handleChildProc() // 啟動上面fork的子進程,并切換到子進程中執行后續代碼
RuntimeInit.zygoteInit() // 創建Binder線程池,一些初始化工作
RuntimeInit.invokeStaticMain() // 利用反射ActivityThread.main()方法
ActivityThread.main() // 創建消息循環,進入消息循環狀態
ActivityThread.main()
public static void main(String[] args) {
......
// 創建消息循環
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// 進入消息循環
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
此時只創建了應用程序的 ActivityThread 和 ApplicationThread,和開啟了 Handler 消息循環機制,其他的都還未創建, ActivityThread.attach(false) 又會最終到 ActivityMangerService 的 attachApplication,這個方法其實是將本地的 ApplicationThread 傳遞到 ActivityMangerService。然后 ActivityMangerService 就可以通過 ApplicationThread 的代理 ApplicationThreadProxy 來調用應用程序 ApplicationThread.bindApplication,通知應用程序的 ApplicationThread 已和 ActivityMangerService 綁定,可以不借助其他進程幫助直接通信了。此時 Launcher 的任務也算是完成了。
本段來源
作者:Jeanboydev
來源:CSDN
原文:https://blog.csdn.net/freekiteyu/article/details/79318031
下面看下ActivityMangerService的attachApplication方法:
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
......
attachApplicationLocked(thread, callingPid);
......
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
// 檢查頂層Activity是否等待被運行
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
// 尋找所有需要在該進程中運行的服務
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
// 檢查是否在這個進程中有下一個廣播接收者
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
......
return true;
}
當發現有Activity需要被啟動時,調用ActivityStackSupervisor.attachApplicationLocked()方法:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
......
realStartActivityLocked(hr, app, true, true);
......
}
后續具體的啟動步驟又回到了啟動一個普通Activity的過程中,同樣可以參考上篇文章Android Activity生命周期,啟動模式,啟動過程詳解。
最后,再引用一張圖片結束本文:
圖片來自 Jeanboydev 來源:CSDN
鏈接:https://blog.csdn.net/freekiteyu/article/details/79318031
至此,我們基本把Android從開機到點擊Icon的過程理清楚了,但是中間每一部分都有很多細節值得深入,因此本文只給了一個大概流程,具體的實現細節可以分模塊去查找探究,本文結束!
參考信息
- 計算機是如何啟動的?
- Android - 系統啟動過程
- 深入分析AIL語言及init.rc文件
- [Android6.0] 啟動流程分析(從開機到第一個程序)
- Android系統啟動-zygote篇
- Android系統啟動-SystemServer上篇
- Android系統啟動-SystemServer下篇
- ActivityManagerService啟動過程
- Android系統進程Zygote啟動過程的源代碼分析
- Android應用程序進程啟動過程的源代碼分析
- Android應用程序啟動過程源代碼分析
- 理解Android進程創建流程
- Android應用程序進程啟動過程(前篇)
- Android點擊應用Icon發生了什么
- 一篇文章看明白 Android 從點擊應用圖標到界面顯示的過程