一. 概述
Android系统将进程做得很友好的封装,对于上层app开发者来说进程几乎是透明的. 了解Android的朋友,一定知道Android四大组件,但对于进程可能会相对较陌生. 一个进程里面可以跑多个app(通过share uid的方式), 一个app也可以跑在多个进程里(通过配置Android:process属性).
再进一步进程是如何创建的, 可能很多人不知道fork的存在. 在我的文章 集中一点详细介绍了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, 见
[-> 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, 见
[-> 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,见
[-> 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, 见.
[-> 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 = '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机制.
转自: