一. 概述
Android系統將進程做得很友好的封裝,對於上層app開發者來說進程幾乎是透明的. 了解Android的朋友,一定知道Android四大組件,但對於進程可能會相對較陌生. 一個進程里面可以跑多個app(通過share uid的方式), 一個app也可以跑在多個進程里(通過配置Android:process屬性).
再進一步進程是如何創建的, 可能很多人不知道fork的存在. 在我的文章理解Android進程創建流程 集中一點詳細介紹了Process.start
的過程是如何一步步創建進程.
進程承載着整個系統,”進程之於Android猶如水之於魚”, 進程對於Android系統非常重要, 對於android來說承載着Android四大組件,承載着系統的正常運轉. 本文則跟大家聊一聊進程的,是從另個角度來全局性講解android進程啟動全過程所涉及的根脈, 先來看看AMS.startProcessLocked方法.
二. 四大組件與進程
2.1 四大組件
Activity, Service, ContentProvider, BroadcastReceiver這四大組件,在啟動的過程,當其所承載的進程不存在時需要調用startProcessLocked先創建進程
2.1.1 Activity
啟動Activity過程: 調用startActivity,該方法經過層層調用,最終會調用ActivityStackSupervisor.java中的startSpecificActivityLocked
,當activity所屬進程還沒啟動的情況下,則需要創建相應的進程.更多關於Activity, 見startActivity啟動過程分析
[-> ActivityStackSupervisor.java]
void startSpecificActivityLocked(...) { ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); if (app != null && app.thread != null) { ... //進程已創建的case return } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); }
2.1.2 Service
啟動服務過程: 調用startService,該方法經過層層調用,最終會調用ActiveServices.java中的bringUpServiceLocked
,當Service進程沒有啟動的情況(app==null), 則需要創建相應的進程. 更多關於Service, 見startService啟動過程分析
[-> ActiveServices.java]
private final String bringUpServiceLocked(...){ ... ProcessRecord app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); if (app == null) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, "service", r.name, false, isolated, false)) == null) { ... } } ... }
2.1.3 ContentProvider
ContentProvider處理過程: 調用ContentResolver.query該方法經過層層調用, 最終會調用到AMS.java中的getContentProviderImpl
,當ContentProvider所對應進程不存在,則需要創建新進程. 更多關於ContentProvider,見理解ContentProvider原理(一)
[-> AMS.java]
private final ContentProviderHolder getContentProviderImpl(...) { ... ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null) { ... //進程已創建的case } else { proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName,cpi.name), false, false, false); } ... }
2.1.4 Broadcast
廣播處理過程: 調用sendBroadcast,該方法經過層層調用, 最終會調用到BroadcastQueue.java中的processNextBroadcast
,當BroadcastReceiver所對應的進程尚未啟動,則創建相應進程. 更多關於broadcast, 見Android Broadcast廣播機制分析.
[-> BroadcastQueue.java]
final void processNextBroadcast(boolean fromMsg) { ... ProcessRecord app = mService.getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid, false); if (app != null && app.thread != null) { ... //進程已創建的case return } if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == null) { ... } ... }
2.2 進程啟動
在ActivityManagerService.java
關於啟動進程有4個同名不同參數的重載方法StartProcessLocked, 為了便於說明,以下4個方法依次記為1(a)
,1(b)
, 2(a)
, 2(b)
:
//方法 1(a) final ProcessRecord startProcessLocked( String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, boolean keepIfLarge) //方法 1(b) final ProcessRecord startProcessLocked( String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) //方法 2(a) private final void startProcessLocked( ProcessRecord app, String hostingType, String hostingNameStr) //方法 2(b) private final void startProcessLocked( ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs)
1(a) ==> 1(b): 方法1(a)將isolatedUid=0,其他參數賦值為null,再調用給1(b)
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, boolean keepIfLarge) { return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType, hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge, null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */, null /* crashHandler */); }
2(a) ==> 2(b): 方法2(a)將其他3個參數abiOverride,entryPoint, entryPointArgs賦值為null,再調用給2(b)
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) { startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */, null /* entryPoint */, null /* entryPointArgs */); }
小結:
- 1(a),1(b)的第一個參數為String類型的進程名processName,
- 2(a), 2(b)的第一個參數為ProcessRecord類型進程記錄信息ProcessRecord;
- 1系列的方法最終調用到2系列的方法;
四大組件所在應用首次啟動時, 調用startProcessLocked方法1(a),之后再調用流程: 1(a) => 1(b) ==> 2(b).
2.3 啟動時機
剛解說了4大組件與進程創建的調用方法,那么接下來再來說說進程創建的觸發時機有哪些?如下:
- 單進程App:對於這種情況,那么app首次啟動某個組件時,比如通過調用startActivity來啟動某個app,則先會觸發創建該app進程,然后再啟動該Activity。此時該app進程已創建,那么后續再該app中內部啟動同一個activity或者其他組件,則都不會再創建新進程(除非該app進程被系統所殺掉)。
- 多進程App: 對於這種情況,那么每個配置過
android:process
屬性的組件的首次啟動,則都分別需要創建進程。再次啟動同一個activity,其則都不會再創建新進程(除非該app進程被系統所殺掉),但如果啟動的是其他組件,則還需要再次判斷其所對應的進程是否存在。
大多數情況下,app都是單進程架構,對於多進程架構的app一般是通過在AndroidManifest.xml中android:process
屬性來實現的。
- 當android:process屬性值以”:”開頭,則代表該進程是私有的,只有該app可以使用,其他應用無法訪問;
- 當android:process屬性值不以”:“開頭,則代表的是全局型進程,但這種情況需要注意的是進程名必須至少包含“.”字符。
接下來,看看PackageParser.java來解析AndroidManiefst.xml過程就明白進程名的命名要求:
public class PackageParser { ... private static String buildCompoundName(String pkg, CharSequence procSeq, String type, String[] outError) { String proc = procSeq.toString(); char c = proc.charAt(0); if (pkg != null && c == ':') { if (proc.length() < 2) { //進程名至少要有2個字符 return null; } String subName = proc.substring(1); //此時並不要求強求 字符'.'作為分割符號 String nameError = validateName(subName, false, false); if (nameError != null) { return null; } return (pkg + proc).intern(); } //此時必須字符'.'作為分割符號 String nameError = validateName(proc, true, false); if (nameError != null && !"system".equals(proc)) { return null; } return proc.intern(); } private static String validateName(String name, boolean requireSeparator, boolean requireFilename) { final int N = name.length(); boolean hasSep = false; boolean front = true; for (int i=0; i<N; i++) { final char c = name.charAt(i); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { front = false; continue; } if (!front) { if ((c >= '0' && c <= '9') || c == '_') { continue; } } //字符'.'作為分割符號 if (c == '.') { hasSep = true; front = true; continue; } return "bad character '" + c + "'"; } if (requireFilename && !FileUtils.isValidExtFilename(name)) { return "Invalid filename"; } return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; } }
看完上面的源碼.很顯然對於android:process屬性值不以”:“開頭的進程名必須至少包含“.”字符。
2.4 小節
Activity, Service, ContentProvider, BroadcastReceiver這四大組件在啟動時,當所承載的進程不存在時,包括多進程的情況,則都需要創建。
四大組件的進程創建方法:
組件 | 創建方法 |
---|---|
Activity | ASS.startSpecificActivityLocked() |
Service | ActiveServices.bringUpServiceLocked() |
ContentProvider | AMS.getContentProviderImpl() |
Broadcast | BroadcastQueue.processNextBroadcast() |
進程的創建過程交由系統進程system_server來完成的.
簡稱:
- ATP: ApplicationThreadProxy
- AT: ApplicationThread (繼承於ApplicationThreadNative)
- AMP: ActivityManagerProxy
- AMS: ActivityManagerService (繼承於ActivityManagerNative)
圖解:
- system_server進程中調用
startProcessLocked
方法,該方法最終通過socket方式,將需要創建新進程的消息告知Zygote進程,並阻塞等待Socket返回新創建進程的pid; - Zygote進程接收到system_server發送過來的消息, 則通過fork的方法,將zygote自身進程復制生成新的進程,並將ActivityThread相關的資源加載到新進程app process,這個進程可能是用於承載activity等組件;
- 創建完新進程后fork返回兩次, 在新進程app process向servicemanager查詢system_server進程中binder服務端AMS,獲取相對應的Client端,也就是AMP. 有了這一對binder c/s對, 那么app process便可以通過binder向跨進程system_server發送請求,即attachApplication()
- system_server進程接收到相應binder操作后,經過多次調用,利用ATP向app process發送binder請求, 即bindApplication.
system_server擁有ATP/AMS, 每一個新創建的進程都會有一個相應的AT/AMS,從而可以跨進程 進行相互通信. 這便是進程創建過程的完整生態鏈.
四. 總結
本文首先介紹AMS的4個同名不同參數的方法startProcessLocked; 緊接着講述了四大組件與進程的關系, Activity, Service, ContentProvider, BroadcastReceiver這四大組件,在啟動的過程,當其所承載的進程不存在時需要先創建進程. 再然后進入重點以startProcessLocked以引線一路講解整個過程所遇到的核心方法. 在整個過程中有新創建的進程與system_server進程之間的交互過程 是通過binder進行通信的, 這里有兩條binder通道分別為AMP/AMN 和 ATP/ATN.
上圖便是一次完整的進程創建過程,app的任何組件需要有一個承載其運行的容器,那就是進程, 那么進程的創建過程都是由系統進程system_server通過socket向zygote進程來請求fork()新進程, 當創建出來的app process與system_server進程之間的通信便是通過binder IPC機制.