一. 概述

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)

图解:

  1. system_server进程中调用startProcessLocked方法,该方法最终通过socket方式,将需要创建新进程的消息告知Zygote进程,并阻塞等待Socket返回新创建进程的pid;
  2. Zygote进程接收到system_server发送过来的消息, 则通过fork的方法,将zygote自身进程复制生成新的进程,并将ActivityThread相关的资源加载到新进程app process,这个进程可能是用于承载activity等组件;
  3. 创建完新进程后fork返回两次, 在新进程app process向servicemanager查询system_server进程中binder服务端AMS,获取相对应的Client端,也就是AMP. 有了这一对binder c/s对, 那么app process便可以通过binder向跨进程system_server发送请求,即attachApplication()
  4. 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四大组件与进程启动的关系(转)的更多相关文章

  1. Android四大组件之Activity--管理方式

    1. 概览 Activity的管理有静态和动态两层涵义: 静态是指Activity的代码组织结构,即Application中声明的Activity的集合,这些Activity被组织在一个APK中,有特 ...

  2. Android四大组件及activity的四大启动模式

    Android四大组件 1. 广播接收者的两种类型: (1)系统广播接收者,就是继承BroadcastReceiver这个类,然后还要在清单文件中注册,注册之后给他一个action.当系统发生了这个a ...

  3. Android四大组件之一“广播”

    前言 Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制.在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就 ...

  4. 【Android 界面效果16】关于android四大组件的总结

    Android四大组件:Activity.Service.Broadcast receiver.Content provider 在Android中,一个应用程序可以使用其它应用程序的组件,这是And ...

  5. Android 四大组件之“ BroadcastReceiver ”

    前言 Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制.在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就 ...

  6. Android四大组件之一 -- Service详解

    相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...

  7. Android四大组件之Service浅见

    Service 是Android四大组件之一,可以在不显示界面的情况下在后台运行.还有一个作用是通过AIDL来实现进程间通信. Service的启动方式 Service的启动方式有两种,startSe ...

  8. Android 四大组件之再论service

    service常见的有2种方式,本地service以及remote service. 这2种的生命周期,同activity的通信方式等,都不相同. 关于这2种service如何使用,这里不做介绍,只是 ...

  9. Android四大组件之Service

    Android四大组件之Service Android支持服务的概念,服务是在后台运行的组件,没有用户界面,Android服务可用有与活动独立的生命周期.Android支持两种类型的服务: 本地服务: ...

随机推荐

  1. Crashlytics Android 异常报告统计管理

    http://www.infoq.com/cn/articles/crashlytics-crash-statistics-tools 简介 Crashlytic 成立于2011年,是专门为移动应用开 ...

  2. vue+Java 前后端分离,多次请求Session不一致的问题(网络上找的)

    在vue main.js中增加以下配置: import axios from 'axios'; axios.defaults.withCredentials=true; 请求时:设置 withCred ...

  3. SQL 中 NOT IN 查询不到数据

    一.问题 用以下sql语句查询数据,结果为空 SELECT a.ID , a.Sub_Project_Name , a.Sub_Project_Type FROM TB_KYSubProject a ...

  4. POJ-3278 抓住这头牛

    广搜解决. 广搜搜出最短路,直接输出返回就行了. 每个点只搜一次,而且界限进行一次判断. else 语句里面不要用if    else if,这样的话就直走一条路了. #include <ios ...

  5. [LUOGU] [NOIP2017] P3960 列队

    题目描述 Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n \times mn×m 名学生,方阵的行 ...

  6. 8. Truncate undo表空间

    8. Truncate undo表空间 要Truncate Undo 表空间,必须为MySQL实例配置至少两个undo表空间(两个undo表空间可确保一个undo表空间保持活动状态,另一个处于脱机状态 ...

  7. MariaDB数据库(一)

    1.数据库简介 1.1 什么是数据库? 简单的说,数据库就是一个存放数据的仓库,这个仓库是按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织,存储的,我们可以通过数据库提供的多种方 ...

  8. blocking and nonblocking assign

    key word: 仿真建模  clock采样block/nonblock blocking时,有时候clk会sample edge后的data: nobocking时,clk sample 以前的d ...

  9. day17-python之文件操作

    1.内置函数 #!/usr/bin/env python # -*- coding:utf-8 -*- # print(abs(-1)) # print(abs(1)) # # print(all([ ...

  10. 分享下找到的Github上大神的EmpireofCode进攻策略:反正我是用了没反应,改了代码后单位不进攻,蠢站在那里,我自己的策略调调能打败不少人,这个日后慢慢研究吧,Github上暂时找到的唯一策略

    from queue import PriorityQueue from enum import Enum from battle import commander from battle impor ...