Android四大组件与进程启动的关系(转)
一. 概述
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机制.
转自:http://gityuan.com/2016/10/09/app-process-create-2/
Android四大组件与进程启动的关系(转)的更多相关文章
- Android四大组件之Activity--管理方式
1. 概览 Activity的管理有静态和动态两层涵义: 静态是指Activity的代码组织结构,即Application中声明的Activity的集合,这些Activity被组织在一个APK中,有特 ...
- Android四大组件及activity的四大启动模式
Android四大组件 1. 广播接收者的两种类型: (1)系统广播接收者,就是继承BroadcastReceiver这个类,然后还要在清单文件中注册,注册之后给他一个action.当系统发生了这个a ...
- Android四大组件之一“广播”
前言 Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制.在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就 ...
- 【Android 界面效果16】关于android四大组件的总结
Android四大组件:Activity.Service.Broadcast receiver.Content provider 在Android中,一个应用程序可以使用其它应用程序的组件,这是And ...
- Android 四大组件之“ BroadcastReceiver ”
前言 Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制.在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就 ...
- Android四大组件之一 -- Service详解
相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...
- Android四大组件之Service浅见
Service 是Android四大组件之一,可以在不显示界面的情况下在后台运行.还有一个作用是通过AIDL来实现进程间通信. Service的启动方式 Service的启动方式有两种,startSe ...
- Android 四大组件之再论service
service常见的有2种方式,本地service以及remote service. 这2种的生命周期,同activity的通信方式等,都不相同. 关于这2种service如何使用,这里不做介绍,只是 ...
- Android四大组件之Service
Android四大组件之Service Android支持服务的概念,服务是在后台运行的组件,没有用户界面,Android服务可用有与活动独立的生命周期.Android支持两种类型的服务: 本地服务: ...
随机推荐
- js前端导出excel
此例子是利用html特性,纯前端导出excel,样式不好看,兼容主流浏览器. var tableid = jQuery(this).closest("div.tab-label-wrap&q ...
- js 漂浮广告
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- UVA12633 Super Rooks on Chessboard
题目描述 题解: 第一眼满眼骚操作,然后全部否掉. 然后屈服于题解,才发现这题这么执掌. 首先,如果这个东西是普通的车,那我们可以记录一下$x,y$的覆盖情况,然后减一下; 但是这个可以斜着走. 所以 ...
- python 05 关于对python中引用的理解
数据的在内存中的地址就是数据的引用. 如果两个变量为同一个引用,那么这两个变量对应的数据一定相同: 如果两个变量对应的数据相同,引用不一定相同. 通过id(数据)可以查看数据对应的地址,修改变量的值, ...
- springmvc请求小例子
1.welcome.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" ...
- python测试工具
https://wiki.python.org/moin/PythonTestingToolsTaxonomy mac pip安装 https://blog.csdn.net/ywj_486/arti ...
- p3386 二分图匹配模板
https://www.luogu.org/problemnew/show/P3386 可以只做一边的匹配 #include <bits/stdc++.h> using namespace ...
- 关于自由拖拽完成的剪切区域(UI组件之图片剪切器)
var x, y,areaWidth,areaHeight; var down;//闪光的判断标准,很好 addEvent(canvas,'mousedown',function(e){ // con ...
- 76. Spring Boot完美解决(406)Could not find acceptable representation原因及解决方法
[原创文章] 使用Spring Boot的Web项目,处理/login请求的控制器方法(该方法会返回JSON格式的数据).此时如果访问localhost:8080/login.html, ...
- Go map基础
package main import "fmt" //Map //创建:make(map[string]int) //获取元素: m[key] //key不存在时,获得value ...