zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    android ndk 调试问题
    音频
    文件分割与c语言文件结
    本机抓包
    rtm匹 转
    mac 工具等效率
    【MySQL】Explain Tutorial
    Sed基本入门[5] Sed Hold and Pattern Space Commands
    Sed基本入门[3] Regular Expressions
    Protocol Buffer Basics
  • 原文地址:https://www.cnblogs.com/zl1991/p/6878722.html
Copyright © 2011-2022 走看看