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先创建进程

component_process

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来完成的.

app_process_ipc

简称:

  • 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.

start_process

上图便是一次完整的进程创建过程,app的任何组件需要有一个承载其运行的容器,那就是进程, 那么进程的创建过程都是由系统进程system_server通过socket向zygote进程来请求fork()新进程, 当创建出来的app process与system_server进程之间的通信便是通过binder IPC机制.

转自:http://gityuan.com/2016/10/09/app-process-create-2/

原文地址:https://www.cnblogs.com/zl1991/p/6878722.html