Android应用框架之应用启动过程详解

在Android的应用框架中,ActivityManagerService是非常重要的一个组件,尽管名字叫做ActivityManagerService,但通过之前的博客介绍,我们知道,四大组件的创建都是有AMS来完成的,其实不仅是应用程序中的组件,连Android应用程序本身也是AMS负责启动的。AMS本身运行在一个独立的进程中,当系统决定要在一个新的进程中启动一个Activity或者Service时就会先启动这个进程。而AMS启动进程的过程是从startProcessLocked启动的。

1.ActivityManagerService.startProcessLocked

public final class ActivityManagerService extends ActivityManagerNative
    implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
  ......
  private final void startProcessLocked(ProcessRecord app,
        String hostingType, String hostingNameStr) {
    ......
    try {
      int uid = app.info.uid;
      int[] gids = null;
      try {
        gids = mContext.getPackageManager().getPackageGids(
          app.info.packageName);
      } catch (PackageManager.NameNotFoundException e) {
        ......
      }
      ......
      int debugFlags = 0;
      ......
      int pid = Process.start("android.app.ActivityThread",
        mSimpleProcessManagement ? app.processName : null, uid, uid,
        gids, debugFlags, null);
      ......
    } catch (RuntimeException e) {
      ......
    }
  }
  ......
}

可以看到,函数会调用Process.start函数来创建一个进程,其中第一个参数”android.app.ActivityThread”是需要加载的类,而在完成这个类的加载之后就会运行ActivityThread.main函数。

2.Process.start

public class Process {
  ......
  public static final int start(final String processClass,
    final String niceName,
    int uid, int gid, int[] gids,
    int debugFlags,
    String[] zygoteArgs)
  {
    if (supportsProcesses()) {
      try {
        return startViaZygote(processClass, niceName, uid, gid, gids,
          debugFlags, zygoteArgs);
      } catch (ZygoteStartFailedEx ex) {
        ......
      }
    } else {
      ......
      return 0;
    }
  }
  ......
}

这个函数最后会调用startViaZygote来创建进程,而Zygote正是Android孵化进程的服务,所有的进程都是通过Zygotefork出来的,所以这里创建进程的任务又落到了Zygote头上了。

3.Process.startViaZygote

public class Process {
  ......
  private static int startViaZygote(final String processClass,
      final String niceName,
      final int uid, final int gid,
      final int[] gids,
      int debugFlags,
      String[] extraArgs)
      throws ZygoteStartFailedEx {
    int pid;

    synchronized(Process.class) {
      ArrayList<String> argsForZygote = new ArrayList<String>();

      // --runtime-init, --setuid=, --setgid=,
      // and --setgroups= must go first
      argsForZygote.add("--runtime-init");
      argsForZygote.add("--setuid=" + uid);
      argsForZygote.add("--setgid=" + gid);
      if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
        argsForZygote.add("--enable-safemode");
      }
      if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
        argsForZygote.add("--enable-debugger");
      }
      if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
        argsForZygote.add("--enable-checkjni");
      }
      if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
        argsForZygote.add("--enable-assert");
      }

      //TODO optionally enable debuger
      //argsForZygote.add("--enable-debugger");

      // --setgroups is a comma-separated list
      if (gids != null && gids.length > 0) {
        StringBuilder sb = new StringBuilder();
        sb.append("--setgroups=");

        int sz = gids.length;
        for (int i = 0; i < sz; i++) {
          if (i != 0) {
            sb.append(',');
          }
          sb.append(gids[i]);
        }

        argsForZygote.add(sb.toString());
      }

      if (niceName != null) {
        argsForZygote.add("--nice-name=" + niceName);
      }

      argsForZygote.add(processClass);

      if (extraArgs != null) {
        for (String arg : extraArgs) {
          argsForZygote.add(arg);
        }
      }
      pid = zygoteSendArgsAndGetPid(argsForZygote);
    }
  }
  ......
}

函数里面最为重要的工作就是组装argsForZygote参数,这些参数将告诉Zygote具体的启动选项,例如”–runtime-init”就表示要为新启动的运行程序初始化运行库。然后调用zygoteSendAndGetPid函数进一步操作。

4.Process.zygoteSendAndGetPid

public class Process {
  ......

  private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
      throws ZygoteStartFailedEx {
    int pid;

    openZygoteSocketIfNeeded();

    try {
      /**
      * See com.android.internal.os.ZygoteInit.readArgumentList()
      * Presently the wire format to the zygote process is:
      * a) a count of arguments (argc, in essence)
      * b) a number of newline-separated argument strings equal to count
      *
      * After the zygote process reads these it will write the pid of
      * the child or -1 on failure.
      */

      sZygoteWriter.write(Integer.toString(args.size()));
      sZygoteWriter.newLine();

      int sz = args.size();
      for (int i = 0; i < sz; i++) {
        String arg = args.get(i);
        if (arg.indexOf('\n') >= 0) {
          throw new ZygoteStartFailedEx(
            "embedded newlines not allowed");
        }
        sZygoteWriter.write(arg);
        sZygoteWriter.newLine();
      }

      sZygoteWriter.flush();

      // Should there be a timeout on this?
      pid = sZygoteInputStream.readInt();

      if (pid < 0) {
        throw new ZygoteStartFailedEx("fork() failed");
      }
    } catch (IOException ex) {
      ......
    }
    return pid;
  }
  ......
}

这里的sZygoteWriter

是一个Socket写入流,是由openZygoteSocketIfNeeded函数打开的。而这个Socket由frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中的ZygoteInit类在runSelectLoopMode函数侦听的。这个类会返回一个ZygoteConnection实例,并执行ZygoteConnection的runOnce函数。

5.ZygoteConnection.runOnce

class ZygoteConnection {
  ......

  boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;

    try {
      args = readArgumentList();
      descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
      ......
      return true;
    }

    ......

    /** the stderr of the most recent request, if avail */
    PrintStream newStderr = null;

    if (descriptors != null && descriptors.length >= 3) {
      newStderr = new PrintStream(
        new FileOutputStream(descriptors[2]));
    }

    int pid;

    try {
      parsedArgs = new Arguments(args);

      applyUidSecurityPolicy(parsedArgs, peer);
      applyDebuggerSecurityPolicy(parsedArgs);
      applyRlimitSecurityPolicy(parsedArgs, peer);
      applyCapabilitiesSecurityPolicy(parsedArgs, peer);

      int[][] rlimits = null;

      if (parsedArgs.rlimits != null) {
        rlimits = parsedArgs.rlimits.toArray(intArray2d);
      }

      pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
        parsedArgs.gids, parsedArgs.debugFlags, rlimits);
    } catch (IllegalArgumentException ex) {
      ......
    } catch (ZygoteSecurityException ex) {
      ......
    }

    if (pid == 0) {
      // in child
      handleChildProc(parsedArgs, descriptors, newStderr);
      // should never happen
      return true;
    } else { /* pid != 0 */
      // in parent...pid of < 0 means failure
      return handleParentProc(pid, descriptors, parsedArgs);
    }
  }
  ......
}

真正创建进程的代码在Zygote.forkAndSpecialize,通过Zygote来fork出一个新的进程作为应用进程。fork函数会有两个返回,其中一个在父进程,一个在子进程,其中自进程的进程号会为0,所以按照上面的代码,这里会执行handleChildProc。

6.ZygoteConnection.handleChildProc

class ZygoteConnection {
  ......
  private void handleChildProc(Arguments parsedArgs,
      FileDescriptor[] descriptors, PrintStream newStderr)
      throws ZygoteInit.MethodAndArgsCaller {
    ......
    if (parsedArgs.runtimeInit) {
      RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
    } else {
      ......
    }
  }
  ......
}

因为在创建的时候传入了“–runtime-init”,所以这里会运行RuntimeInit.zygoteInit。

public class RuntimeInit {
  ......

  public static final void zygoteInit(String[] argv)
      throws ZygoteInit.MethodAndArgsCaller {
    // TODO: Doing this here works, but it seems kind of arbitrary. Find
    // a better place. The goal is to set it up for applications, but not
    // tools like am.
    System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
    System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));

    commonInit();
    zygoteInitNative();

    int curArg = 0;
    for ( /* curArg */ ; curArg < argv.length; curArg++) {
      String arg = argv[curArg];

      if (arg.equals("--")) {
        curArg++;
        break;
      } else if (!arg.startsWith("--")) {
        break;
      } else if (arg.startsWith("--nice-name=")) {
        String niceName = arg.substring(arg.indexOf('=') + 1);
        Process.setArgV0(niceName);
      }
    }

    if (curArg == argv.length) {
      Slog.e(TAG, "Missing classname argument to RuntimeInit!");
      // let the process exit
      return;
    }

    // Remaining arguments are passed to the start class's static main

    String startClass = argv[curArg++];
    String[] startArgs = new String[argv.length - curArg];

    System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
    invokeStaticMain(startClass, startArgs);
  }
  ......
}

这里有两个关键的函数调用,一个是zygoteInitNative函数调用,一个是invokeStaticMain函数调用,前者就是执行Binder驱动程序初始化的相关工作了,正是由于执行了这个工作,才使得进程中的Binder对象能够顺利地进行Binder进程间通信,而后一个函数调用,就是执行进程的入口函数,这里就是执行startClass类的main函数了,而这个startClass即是我们在Step 1中传进来的”android.app.ActivityThread”值,表示要执行android.app.ActivityThread类的main函数。

7. Zygote.invokeStaticMain

public class ZygoteInit {
  ......

  static void invokeStaticMain(ClassLoader loader,
      String className, String[] argv)
      throws ZygoteInit.MethodAndArgsCaller {
    Class<?> cl;

    try {
      cl = loader.loadClass(className);
    } catch (ClassNotFoundException ex) {
      ......
    }

    Method m;
    try {
      m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
      ......
    } catch (SecurityException ex) {
      ......
    }

    int modifiers = m.getModifiers();
    ......

    /*
    * This throw gets caught in ZygoteInit.main(), which responds
    * by invoking the exception's run() method. This arrangement
    * clears up all the stack frames that were required in setting
    * up the process.
    */
    throw new ZygoteInit.MethodAndArgsCaller(m, argv);
  }
  ......
}

从代码中可以看到,通过ClassLoader加载对应的android.app.ActivityThread类,然后再获取到对应的main函数句柄,最后调用该类的main函数。不过这里的调用方式比较有意思,不知直接调用,而是通过抛出一个异常。这样做的方式是为了清空堆栈,让系统认为新进程是从ActivityThread的main函数开始的。

8.ActivityThread.main

public final class ActivityThread {
  ......

  public static final void main(String[] args) {
    SamplingProfilerIntegration.start();

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();
    if (sMainThreadHandler == null) {
      sMainThreadHandler = new Handler();
    }

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (false) {
      Looper.myLooper().setMessageLogging(new
        LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();

    if (Process.supportsProcesses()) {
      throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    thread.detach();
    String name = (thread.mInitialApplication != null)
      ? thread.mInitialApplication.getPackageName()
      : "<unknown>";
    Slog.i(TAG, "Main thread of " + name + " is now exiting");
  }

  ......
}

从这里我们可以看出,这个函数首先会在进程中创建一个ActivityThread对象,然后进入消息循环中,这样,我们以后就可以在这个进程中启动Activity或者Service了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 解析android创建快捷方式会启动两个应用的问题

    接下来讲一种完美的方案.因为第一个界面是欢迎界面,所以快捷方式启动的界面也是欢迎界面,刚开始就怀疑快捷方式创建的代码有问题,但不知道问题出在哪儿.猜测可能会是快捷方式的错误导致系统创建两个应用,就也是同一个应用会有两个PID,结果在控制台查看打印的LOG,不同方式启动的LOG如下: 复制代码 代码如下: 09-22 09:39:11.929: INFO/ActivityManager(61): Starting: Intent { act=android.intent.action.MAIN c

  • 解析Android应用启动后自动创建桌面快捷方式的实现方法

    要不怎么说Android特别开放呢,在Android开发中,只要发送一个广播,就可以实现这种需求了. 废话不多说,以下是封装好的一段代码. 复制代码 代码如下: public class ShortcutUtil { public static void createShortCut(Activity act, int iconResId,              int appnameResId) { // com.android.launcher.permission.INSTALL_SH

  • Android笔记之:App应用之启动界面SplashActivity的使用

    当前比较成熟一点的应用基本上都会在进入应用之显示一个启动界面.这个启动界面或简单,或复杂,或简陋,或华丽,用意不同,风格也不同.下面来观摩几个流行的应用的启动界面. 1. 货比三家以腾讯qq,新浪weibo,UC浏览器,游戏神庙逃亡等7个应用为例,比比看:(我认为最精美的界面应该是qq2012,虽然只有一张图,基本的应用名称,版本,图标这些信息都有,但是看着舒服,觉得美.) 2. 元素启动界面的本意是以友好用户界面来掩饰后台缓冲加载,让用户用平和等待的心情进入正常应用界面.但是因为启动界面是放在

  • Android使用Intent启动其他非系统应用程序的方法

    本文实例讲述了Android使用Intent启动其他非系统应用程序的方法.分享给大家供大家参考,具体如下: android应用程序内部通过Intent来实现Activity间的跳转.也知道通过Intent调用系统程序.但若想在应用程序A内开启应用程序B(前提是A.B均已安装),该如何去实现? 记录下实现过程. 在应用程序A内添加如下代码: Intent i = new Intent(); i.setClassName("com.example.a", "com.example

  • Android优化应用启动速度

    一.应用的启动 启动方式 通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动. 特点 1.冷启动:冷启动因为系统会重新创建一个新的进程分配给它,

  • android应用实现开机自动启动方法

    原理:Android系统在开机的时候会发出一个广播.这样我们就可以接收这个广播,然后启动我们的应用.广播接收器必须在xml里面配置,因为xml里面配置的广播接收器  是不随着应用的退出而退出的. 广播接收器: package com.yangshidesign.boot; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public c

  • 分析Android中应用的启动流程

    前言 在我们开始之前,希望您能最好已经满足以下条件: 1.有一份编译后的Android源码(亲自动手实践才会有更深入的理解) 2.对Binder机制有一定的了解 本文启动流程分析基于Android 5.1的源码.为什么是5.1的源码呢?因为手边编译完的代码只有这个版本-另外,用什么版本的源码并不重要,大体的流程并无本质上的区别,仅仅是实现细节的调整,找一个你熟悉的版本就好. 1.启动时序图 作为一个轻微强迫症的人,整理的时序图,相信大家按图索骥,一定能搞明白整个启动流程: 说明:为了让大家更清楚

  • Android Intent启动别的应用实现方法

    我们知道Intent的应用,可以启动别一个Activity,那么是否可以启动别外的一个应用程序呢,答案是可以的. 1.首先我们新建一个Android应用,名为AnotherPro,此应用什么内容都没有,用于被另外一个程序打开. 2.新建一个工程用于打开上面的应用,程序界面如下 3.修改程序代码,在onCreate中添加如下代码 anotherPro = (Button) findViewById(R.id.startAnotherPro);calendar = (Button) findView

  • Android 启动activity的4种方式及打开其他应用的activity的坑

    Android启动的四种方式分别为standard,singleTop,singleTask,singleInstence. standard是最常见的activity启动方式,也是默认的启动的方式.当启动一个activity的时候他将进入返回栈的栈顶.系统不会管栈内是否有相同的activity,方式像后入先出. singleTop方式是在活动启动的时候,系统先判定栈顶是否有相同的活动,如果没有则新建活动,否则将不新建活动.而是直接使用他. singleTask方式在活动启动的时候,系统先判定栈

  • Android应用启动另外一个apk应用的方法

    本文实例讲述了Android应用启动另外一个apk应用的方法.分享给大家供大家参考,具体如下: 在开发的过程中,经常会遇到在一个应用中启动另外一个apk应用的情况 问题的核心点在于我们要拿到第三方apk的package名称跟class名称, 如:package名称是com.funcity.taxi.passenger,class名称是com.funcity.taxi.passenger.activity.LoadActivity. 从一个apk启动到另外一个apk,当然也是通过发送intent了

随机推荐