Android学习之路(14) AMS与PMS详解

news/2024/10/31 3:19:46/

Android 系统启动流程与 Zygote、SystemServer

在讲解 Zygote 之前,考虑到不同的系统版本源码都不相同,以下分析的源码基于 Android 8.0.0。

init 进程

当系统启动时,init 进程是继 Linux 内核启动后第二个启动的进程,它是在用户空间被创建的进程,可以通过命令 adb shell ps 查看 init 进程的 pid:

上图中 PID 是当前进程的 id,PPID 是父进程的 id,并且 Linux 的进程 PID 是按启动顺序从前往后排序。

init 进程在上图中的 pid=1,而 Linux 内核的 pid=0,这也是验证了 init 进程是继 Linux 内核启动后启动的下一个进程。

init 进程主要有两个作用:

  • 启动系统关键的服务

  • 守护关键服务,如果其中一个关键服务被杀死,将会重启手机

怎样的服务属于关键服务?关键服务是对于手机而言必不可少的服务,比如网络服务、蓝牙服务、铃声服务等,同样可以通过 adb shell ps 查看 ppid=1 的其他服务,就是由 init 进程启动守护的关键服务:

上图中例如 installd、servicemanager、surfaceflinger 等 ppid=1 的都是由 init 进程启动的关键服务。

所以 如果要实现一个系统服务又不想被杀死,最好的方式就是让服务由 init 进程启动成为关键服务。

init 进程也是一段可执行的程序,所以也有对应的相关代码。init.c 代码具体是在 Android 源码目录 /system/core/init/init.cpp。

而我们常说的 init.rc,你可以理解为它是 init 进程要执行的任务清单,其实就是一个执行脚本:

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // 启动 zygote 的执行脚本...

可以看到 init.rc 任务清单中其实也是导入的其他待执行的任务清单的文件路径,其中就有启动 zygote 的执行脚本,ro.zygote 会根据系统类型获取对应的执行脚本文件,例如 32 位获取的就是 init.zygote32.rc,64 位获取的就是 init.zygote64.rc:

system/core/rootdir/init.zygote32.rcservice zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks

第一行脚本信息记录的要执行的程序入口位置,会先执行 /system/bin/app_process 下的 app_main.c,-Xzygote /system/bin --zygote --start-system-server 是入口程序传入的参数。

native 层启动 Zygote

上面提到,init.rc 是一段执行脚本,其中就有启动 Zygote 进程的执行文件 init.zygote32.rc 或 init.zygote64.rc,该文件会执行 /frameworks/base/cmds/app_process/app_main.c 的 main() 函数启动 Zygote:

/frameworks/base/cmds/app_process/app_main.c#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif// argv 就是脚本传入的参数 -Xzygote /system/bin --zygote --start-system-server
int main(int argc, char* const argv[]) {...while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) {startSystemServer = true;} ...}...for (; i < argc; ++i) {args.add(String8(argv[i]));}// 将 app_process 修改为 zygoteif (!niceName.isEmpty()) {runtime.setArgv0(niceName.string(), true /* setProcName */);}if (zygote) {// 在 system/core/rootdir/init.zygote32.rc(或 init.zygote.64.rc) 解析到参数 --zygote,所以 zygote == true,通过 runtime.start() 创建 Zygote 进程runtime.start("com.android.internal.os.ZygoteInit", args, zygote);}...
}

上面的代码 将 app_process 修改为了 Zygote,所以 Zygote 一开始并不是这个名称,而是在启动时才被修改为 Zygote。

Zygote 虽然是在 Framework native 层由 C 语言的 main() 入口执行创建的,因为 init 进程是在用户空间,init 进程创建了 Zygote,所以 Zygote 是在用户空间。

我们接着看 runtime.start() 做了什么事情:

/frameworks/base/core/jni/AndroidRuntime.cppvoid AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...// 创建虚拟机,startVm() 有很多虚拟机的参数配置,比如内存大小// 内存调优和其他虚拟机的调优就是在这个函数处理/* start the virtual machine */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote) != 0) {return;}onVmCreated(env);// 动态注册 java 调用 native 的 jni// 我们在写 java 代码时能声明 native 方法调用 C/C++ 函数,就是因为在这里做了注册处理了映射关系/** Register android functions.*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}...// 调用 com.android.internal.os.ZygoteInit 的 main 方法jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);}...
}

runtime.start() 主要做了三件事情:

  • startVm() 创建虚拟机

  • startReg() 动态注册 java 调用 native 的 jni

  • 反射调用 ZygoteInit 的 main()

通过 startVm() 创建虚拟机,startVm() 有很多虚拟机的参数配置,比如堆内存大小,如果是系统工程师,内存调优和其他虚拟机的调优就是在这个函数处理。

startReg() 动态注册 java 调用 native 的 jni,我们在写 java 代码时能声明 native 方法调用 C/C++ 函数,就是因为在这里做了注册处理了映射关系。以 MessageQueue 为例子:

// 在 startReg() 动态注册,将 java 和 C/C++ 的函数关联
static const JNINativeMethod gMessageQueueMethods[] = {{."nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },...
};int register_android_os_MessageQueue(JNIEnv* env) {...
}

最终就是调用 ZygoteInit 的 main() 方法,到这里就开始进入 java 的世界。

我们简单总结下 Zygote 在 native 的处理流程,如下图:

Java 层启动 ZygoteInit

在 native 创建了 Zygote,并且通过 AndroidRuntime.start() 从 native 层转到 java 层 ZygoteInit 的 main() 入口继续处理 Zygote 相关流程:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

// argv 就是 init.{ro.zygote}.rc 脚本写的参数
// -Xzygote /system/bin --zygote --start-system-server
public static void main(String argv[]) {// 创建 ServerSocketZygoteServer zygoteServer = new ZygoteServer();...boolean startSystemServer = false;String socketName = "zygote";String abiList = null;boolean enableLazyPreload = false;for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {startSystemServer = true;} else if ("--enable-lazy-preload".equals(argv[i])) {enableLazyPreload = true;} else if (argv[i].startsWith(ABI_LIST_ARG)) {abiList = argv[i].substring(ABI_LIST_ARG.length());} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {socketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}...}zygoteServer.registerServerSocket(socketName);...// 加载系统类、系统资源等if (!enableLazyPreload) {...preload(bootTimingsTraceLog);...}...// 创建 SystemServerif (startSystemServer) {startSystemServer(abiList, socketName, zygoteServer);}// ZygoteServer 创建 ServerSocket 作为服务器// 开启循环,等待接收 socket 通信 fork app 进程的请求// 没有消息会一直阻塞休眠等待,Zygote 进程会一直存活运行zygoteServer.runSelectLoop(abiList);zygoteServer.closeServerSocket();...
}

在 ZygoteInit 的 main() 入口方法主要做了三件事情:

  • 预先加载系统资源,如系统类、资源、系统共享库等
  • 创建 ZygoteServer,其实就是 ServerSocket 循环等待通知 fork 子进程
  • 创建 SystemServer 进程

预加载资源

我们先看下预先加载资源 preload() 做了什么事情:

static void preload(TimingsTraceLog bootTimingsTraceLog) {...// 系统类加载preloadClasses();...// 系统资源加载preloadResources();...// openGL加载preloadOpenGL();...// 系统共享库加载preloadSharedLibraries();// 文字资源加载preloadTextResources();...
}private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";private static void preloadClasses() {...	InputStream is;try {// 获取要加载的系统资源文件流is = new FileInputStream(PRELOADED_CLASSES);} catch (FileNotFoundException e) {Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");return;}...BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);int count = 0;String line;while ((line = br.readLine()) != null) {...try {// 读取 /system/etc/preloaded-classes 的类路径并加载Class.forName(line, true, null);}...}	...
}public static final boolean PRELOAD_RESOURCES = true;private static void preloadResources() {...mResources = Resources.getSystem();mResources.startPreloading();if (PRELOAD_RESOURCES) {TypedArray ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_drawables);int N = preloadDrawables(ar);ar.recycle();ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_color_state_lists);N = preloadColorStateLists(ar);ar.recycle();...}mResources.finishPreloading();
}

preload() 主要是预先加载了系统类、系统资源、系统共享库、openGL、文字资源等,其中系统类是读取的 preloaded-classes。因为系统类较多,下面只截取了文件的一部分,具体可以在源码查看该文件:

frameworks/base/preloaded-classes...
android.app.Activity
android.app.Activity$HostCallbacks
android.app.ActivityManager
android.app.ActivityManager$1
android.app.ActivityManager$RecentTaskInfo
android.app.ActivityManager$RecentTaskInfo$1
android.app.ActivityManager$RunningAppProcessInfo
android.app.ActivityManager$RunningAppProcessInfo$1
android.app.ActivityManager$RunningServiceInfo
android.app.ActivityManager$RunningServiceInfo$1
android.app.ActivityManager$RunningTaskInfo
android.app.ActivityManager$RunningTaskInfo$1
android.app.ActivityManager$StackId
android.app.ActivityManager$TaskDescription
android.app.ActivityManager$TaskDescription$1
android.app.ActivityOptions
android.app.ActivityThread
android.app.ActivityThread$1
android.app.ActivityThread$2
android.app.ActivityThread$ActivityClientRecord
android.app.ActivityThread$ActivityConfigChangeData
android.app.ActivityThread$AppBindData
android.app.ActivityThread$ApplicationThread
android.app.ActivityThread$BindServiceData
android.app.ActivityThread$ContextCleanupInfo
android.app.ActivityThread$CreateServiceData
android.app.ActivityThread$DropBoxReporter
android.app.ActivityThread$EventLoggingReporter
android.app.ActivityThread$GcIdler
android.app.ActivityThread$H
...

可以发现这些全类名路径就是我们在 app 开发中使用的四大组件 Activity、Fragment、常用控件 TextView 等。

所以我们 app 运行的时候,字体库、资源、系统类就是从这里来的,因为 Zygote 启动时已经提前预先加载好了。

在 Zygote 预先加载这些资源的好处是,不需要每个 app 都去加载这些资源,而是使用提前预先加载好的这些类和资源,可以直接使用。

需要注意的是,因为 preoload() 是在主进程调用的,而且会比较耗时,如果要对系统启动速度做优化,也可以从这个方法入手。

创建 ZygoteServer 循环等待 fork 子进程

首先我们要明白,什么是 fork?fork 可以理解为就是复制,所以 Zygote fork 进程其实就是在 Zygote 基础上复制一个进程作为子进程,子进程拥有 Zygote 已经处理好的资源。

Zygote 其中的一个职责是负责 fork 子进程的创建,比如要接收 AMS 通过 socket 通信告知创建 app 进程,此时 AMS 是客户端,Zygote 作为服务端要接收 socket 消息,就需要创建 ServerSocket 服务器循环等待。

class ZygoteServer {private LocalServerSocket mServerSocket;void registerServerSocket(String socketName) {if (mServerSocket == null) {...try {FileDescriptor fd = new FileDescriptor();fd.setInt$(fileDesc);// 创建 ServerSocketmServerSocket = new LocalServerSocket(fd);} catch (IOException ex) {...}}}void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {...// 循环等待 socket 消息通知 fork 进程while (true) {...try {// 没有消息休眠等待Os.poll(pollFds, -1);} catch (ErrnoException ex) {...}for (int i = pollFds.length - 1; i >= 0; --i) {...boolean done = peers.get(i).runOnce(this);...}}}
}ZygoteConnection.javaboolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {...// fork 子进程pid = Zygote.forkAndSpecialize(...);...
}Zygote.javapublic static int forkAndSpecialize(...) {...int pid = nativeForkAndSpecialize(...);...return pid;
}native private static int nativeForkAndSpecialize(...);frameworks/base/core/jni/com_android_internal_os_Zygote.cppstatic jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {...return ForkAndSpecializeCommon(...);
}static pid_t ForkAndSpecializeCommon(...) {...// 调用 Linux 的 fork() 创建进程pid_t pid = fork();...
}

通过源码可以分析到,ZygoteServer 其实就是创建了一个 ServerSocket,在 ZygoteInit 调用 runSelectLoop() 作为服务端等待客户端 socket 通信告知 fork 进程,fork 进程是转到 native 层最终调用 Linux 标准函数 fork()。

创建 SystemServer 进程

在 ZygoteInit 会创建 SystemServer 进程,SystemServer 是 Zygote 创建的第一个子进程:

ZygoteInit.javaprivate static boolean startSystemServer(...) {...pid = Zygote.forkSystemServer(...);...return true;
}

关于如何 fork 在上一小节已经介绍,forkSystemServer() 只是创建了 SystemServer 进程,那么 SystemServer 是什么时候运行的呢?接着往下分析源码:

ZygoteInit.javaprivate static boolean startSystemServer(...) {...pid = Zygote.forkSystemServer(...);...if (pid == 0) {...handleSystemServerProcess(parsedArgs);}return true;
}private static void handleSystemServerProcess(...) {...ZygoteInit.zygoteInit(...);
}public static final void zygoteInit(...) {...// 初始化运行环境RuntimeInit.commonInit(); // 打开 Binder 驱动,初始化 BinderZygoteInit.nativeZygoteInit(); // 反射调用 main() 入口函数RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}RuntimeInit.javaprotected static void applicationInit(...) {...// Remaining arguments are passed to the start class's static maininvokeStaticMain(args.startClass, args.startArgs, classLoader);
}private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {Class<?> cl;try {cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {...}Method m;try {// 反射调用 main() 方法m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {...}...
}

SystemServer 进程的创建最终是通过反射 main() 函数执行。

SystemServer 是干嘛用的?继续分析 SystemServer 的源码。

SystemServer.javapublic static void main(String[] args) {new SystemServer().run();
}private void run() {...// system_server 进程启动服务管理类mSystemServiceManager = new SystemServiceManager(mSystemContext);	try {// 启动引导服务startBootstrapServices();// 启动核心服务startCoreServices();// 启动其他服务startOtherServices();...}...
}private void startBootstrapServices() {...// 启动 AMS 服务,在高版本是 ATMSmActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();mActivityManagerService.setSystemServiceManager(mSystemServiceManager);mActivityManagerService.setInstaller(installer);	...// 启动 PMSmPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();...
}private void startCoreServices() {// 为了能让 SystemServiceManager 统一的方式管理服务,通过 SystemService 代理这些服务mSystemServiceManager.startService(DropBoxManagerService.class);mSystemServiceManager.startService(BatteryService.class);mSystemServiceManager.startService(UsageStatsService.class);mActivityManagerService.setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
}private void startOtherServices() {...// 各种系统信息的管理,例如字体、系统设置、开发者选项等mActivityManagerService.installSystemProviders();...
}

SystemServer 其实是启动引导服务、核心服务和其他服务的入口,可以发现这些服务并不是通过 Zygote fork,而是直接 new 创建出来的,所以 这些服务都在 system_server 进程。

因为启动的服务较多,所以 SystemServer 创建了 SystemServiceManager 管理这些服务。

为了能统一的方式处理,这些服务通过 SystemService 代理的方式提供给 SystemServiceManager 管理。

在应用层各个进程都需要用到这些服务,那么这些进程是怎么获取到这些服务的?

我们以电量服务 BatteryService 举例:

public abstract class SystemService {...public abstract void onStart();...protected final void publishBinderService(String name, IBinder service,boolean allowIsolated, int dumpPriority) {// 将服务注册到 ServiceManagerServiceManager.addService(name, service, allowIsolated, dumpPriority);}
}// 服务通过 SystemService 代理
public final class BatteryService extends SystemService {@Overridepublic void onStart() {...// 注册服务到 ServiceManagermBinderService = new BinderService();publishBinderService("battery", mBinderService);...}
}public final class SystemServer {private void startCoreServices() {...mSystemServiceManager.startService(BatteryService.class);...}	
}public class SystemServiceManager {@SuppressWarnings("unchecked")public <T extends SystemService> T startService(Class<T> serviceClass) {try {final String name = serviceClass.getName();...final T service;try {Constructor<T> constructor = serviceClass.getConstructor(Context.class);service = constructor.newInstance(mContext);} catch (...) {...}    startService(service);return service;} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}}public void startService(@NonNull final SystemService service) {// Register it.mServices.add(service);// Start it.long time = SystemClock.elapsedRealtime();try {service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + service.getClass().getName()+ ": onStart threw an exception", ex);}warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");}
}

可以看到这些服务经过 SystemServiceManager 统一管理后,最终都会把服务注册到 ServiceManager,ServiceManager 记录着 key 为服务名称 value 为服务的 Binder 列表,应用层进程根据服务名称就可以很方便的拿到要通信的 Binder。

ServiceManager 是一个独立的进程,它和 Zygote 一样也是在 init.rc 脚本执行时启动的独立进程。

我们再简单梳理下 SystemServer 做了哪些事情:

  • 创建 SystemServiceManager 用于统一管理服务

  • 启动各种服务如 AMS、PMS 等

  • 将启动的服务注册到 ServiceManager

SystemServer 流程图如下:

子进程启动时 Binder 初始化

Zygote fork 子进程启动子进程时会初始化 Binder,比如打开 Binder 驱动,每个进程都有自己的 Binder,具体调用是在子进程的 main() 函数执行之前调用 nativeZygoteInit():

ZygoteInit.javapublic static final void zygoteInit(...) {... // 创建 BinderZygoteInit.nativeZygoteInit(); ...
}private static final native void nativeZygoteInit();frameworks/base/core/jni/AndroidRuntime.cppstatic void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{gCurRuntime->onZygoteInit();
}int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
{const JNINativeMethod methods[] = {{ "nativeZygoteInit", "()V",(void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },};return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",methods, NELEM(methods));
}frameworks/base/cmds/app_process/app_main.cppvirtual void onZygoteInit()
{// 打开 Binder 驱动sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");// 启动 Binder 线程池proc->startThreadPool();
}frameworks/native/libs/binder/ProcessState.cpp// Binder 通信数据大小 1M-8k
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
// 最大 Binder 线程数量 15
#define DEFAULT_MAX_BINDER_THREADS 15sp<ProcessState> ProcessState::self()
{Mutex::Autolock _l(gProcessMutex);if (gProcess != NULL) {return gProcess;}gProcess = new ProcessState("/dev/binder");return gProcess;
}static int open_driver(const char *driver)
{// 打开 Binder 驱动int fd = open(driver, O_RDWR | O_CLOEXEC);if (fd >= 0) {int vers = 0;status_t result = ioctl(fd, BINDER_VERSION, &vers);if (result == -1) {ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));close(fd);fd = -1;}if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",vers, BINDER_CURRENT_PROTOCOL_VERSION, result);close(fd);fd = -1;}size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);if (result == -1) {ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));}} else {ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));}return fd;
}ProcessState::ProcessState(const char *driver): mDriverName(String8(driver)), mDriverFD(open_driver(driver)) // 打开 binder 驱动, mVMStart(MAP_FAILED), mThreadCountLock(PTHREAD_MUTEX_INITIALIZER), mThreadCountDecrement(PTHREAD_COND_INITIALIZER), mExecutingThreadsCount(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), mStarvationStartTimeMs(0), mManagesContexts(false), mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL), mThreadPoolStarted(false), mThreadPoolSeq(1)
{if (mDriverFD >= 0) {// mmap 内存映射// mmap the binder, providing a chunk of virtual address space to receive transactions.mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);if (mVMStart == MAP_FAILED) {// *sigh*ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");close(mDriverFD);mDriverFD = -1;mDriverName.clear();}}LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

在这段代码我们能了解到很多 Binder 相关的东西:

  • 每个由 Zygote fork 的进程都会初始化自己的 Binder
  • Binder 有线程池是多线程的,因为进程 A 可能有多个进程与它通信,所以设计是并发的
  • Binder 通信最大的数据大小是 1M - 8k
  • Binder 线程最大数量是 15 个

AMS 使用 socket 通知 Zygote fork 进程也是走的这块流程。

总结

我们从系统启动 init 进程开始分析到 Zygote 进程创建、SystemServer 进程创建,大致的将整体流程进行了梳理。

init 进程是系统内核启动后第二个启动的进程,该进程在用户空间。init 进程启动后会解析 init.rc 执行脚本启动 Zygote 进程和 ServiceManager 进程。

Zygote 因为是由 init 进程启动的,所以它也是在用户空间。Zygote 的业务处理横跨 native 层和 java 层。

Zygote 在 native 层处理的事情:

  • startVm() 创建虚拟机
  • startReg() 动态注册 java 调用 native 的 jni
  • 反射调用 ZygoteInit 的 main()

Zygote 在 java 层处理的事情:

  • 预先加载系统资源,如系统类、资源、系统共享库等
  • 创建 ZygoteServer,其实就是 ServerSocket 等待通知 fork 子进程
  • 创建 SystemServer 进程

Zygote 从创建到启动总体流程如下图:

Zygote fork 的第一个子进程是 SystemServer。

SystemServer 处理的事情:

  • 创建 SystemServiceManager 用于统一管理服务
  • 启动各种服务如 AMS、PMS 等
  • 将启动的服务注册到 ServiceManager

SystemServer 流程图如下:

也简单说明了 Zygote fork 子进程时 Binder 的初始化过程,Binder 的初始化是在进程 fork 完成主入口 main() 方法执行之前处理。通过简单的流程分析能了解到 Binder 的一些信息:

  • 每个由 Zygote fork 的进程都会初始化自己的 Binder
  • Binder 有线程池是多线程的,因为进程 A 可能有多个进程与它通信,所以设计是并发的
  • Binder 通信最大的数据大小是 1M - 8k
  • Binder 线程最大数量是 15 个

系统启动整体流程如下图:

常见问题

1、Zygote 进程最原始的进程是什么进程(或者 Zygote 的由来)?【Zygote 进程最开始的名字】

Zygote 最开始是 app_process,它是在 init 进程启动时被启动的,在 app_main.c 才被修改为 Zygote。

2、Zygote 是在内核空间还是在用户空间?

因为 init 进程的创建在用户空间,而 Zygote 是由 init 进程创建启动的,所以 Zygote 是在用户空间。

3、app 的进程启动,为什么是从 Zygote fork,而不是从 init 进程 fork?

Zygote 从创建到启动做了很多事情,比如创建虚拟机,注册 jni,预加载资源等等,fork 进程其实就是复制进程,如果不在 Zygote fork 进程,那么新创建 app 进程就要重新对以上流程再做一遍,而如果从 Zygote fork 子进程,app 进程创建运行就可以直接使用相关资源,不需要再处理。

而 init 进程主要做的事情是挂载文件(识别各类文件,相当于解析硬盘)、解析 init.rc、处理脚本(启动 Zygote、ServiceManager 进程等)。

4、Zygote 为什么用 socket 通信而不是 Binder?

目前网络上有两种说法:一种是会导致死锁,另一种是会导致读写错误。

(1)Zygote 用 binder 通信会导致死锁

假设 Zygote 使用 Binder 通信,因为 Binder 是支持多线程的,存在并发问题,而并发问题的解决方案就是加锁,如果进程 fork 是在多线程情况下运行,Binder 等待锁在锁机制下就可能会出现死锁。

为什么会出现死锁呢?我们可以用一个场景来分析。

假设是 AMS 使用 Binder 通信告知 Zygote fork 一个 app 进程,为了保证不会出现并发问题,AMS 和 Zygote 的通信会加锁,AMS 要和 Zygote 通信拿的 Binder 是属于 Zygote 的(获取的要通信方的 Binder 代理),此时 Zygote fork 了进程,会连带把 Binder 等待锁的状态也复制过去,那么子进程的 Binder 加了锁由谁来解锁?子进程没有解锁,就会出现死锁。

再从 fork 的原理上分析。

在内存区域里,静态变量 mutex 的内存会被拷贝到子进程里,而且父进程里即使存在多个线程,但它们也不会被继承到子进程里,fork 的这两个特征就是造成死锁的原因。

  • 线程里的 doit() 先执行
  • doit 执行的时候会给互斥体变量 mutex 加锁
  • mutex 变量的内容会原样拷贝到 fork 出来的子进程中(在此之前,mutex 变量的内容已经被线程改写成锁定状态)
  • 子进程再次调用 doit 的时候,在锁定互斥体 mutex 的时候会发现它已经被加锁,所以就一直等待,直到拥有该互斥体的进程释放它(实际上没有人拥有这个 mutex 锁)
    线程的 doit 执行完成之前会把自己的 mutex 释放,但这是的 mutex 和子进程里的 mutex 已经是两份内存,所以即使释放了 mutex 锁也不会对子进程里的 mutex 造成什么影响,最终导致死锁

(2)Zygote 用 binder 通信会导致读写错误

根本原因在于要 new 一个 ProcessState 用于 Binder 通信时,需要 mmap 申请一片内存用以提供给内核进行数据交换使用。

而如果直接 fork 了的话,子进程在进行 binder 通信时,内核还是会继续使用父进程申请的地址写数据,而此时会触发子进程 COW(Copy on Write),从而导致地址空间已经重新映射,而子进程还尝试访问之前父进程 mmap 的地址,会导致 SIGSEGV、SEGV_MAPERR段错误。

可以自己写一个 demo 去尝试,提供一个JNI接口用来调用 fork,待 pid == 0 时,继续使用父进程已获取的binder对象进行binder调用,你就会获得这个段错误。

5、ServiceManager 和 SystemServiceManager 的关系?

ServiceManager 和 SystemServiceManager 没有关系。

ServiceManager 是一个独立进程,和 Zygote 一样通过 init.rc 执行脚本启动,在 SystemServer 启动的服务最终会注册到 ServiceManager 提供给上层使用。

SystemServiceManager 是在 SystemServer 创建的用于在 system_server 进程管理启动服务的管理类。

Android PMS(PackageManagerService) 原理

什么是 PMS

PMS(PackageManagerService)是 Android 提供的包管理系统服务,它用来管理所有的包信息,包括应用安装、卸载、更新以及解析 AndroidManifest.xml。通常情况下我们不会把 PMS 单独的拆分出来讲解,因为 PMS 最主要的是提供给 AMS(ActivityManagerService)服务。

你是否有考虑过为什么我们手机开启启动时会很慢?这是因为 在手机启动时 PMS 会在这段时间处理 apk 解析,至少有 70% 的启动时间耗费在 PMS 解析上,所以这也是为什么手机开机启动比较慢的原因之一。

从解析的角度上,可以理解为 PMS 保存了后续提供给 AMS 所需要的数据,它是具有保存应用数据的缓存。

AndroidManifest.xml 的作用

当手机开机的时候,系统启动 PMS 后会去扫描两个目录,分别是存放用户安装的 apk 的目录 /data/app 以及系统安装的 apk 的目录 /system/app。

刚才有提到,PMS 是为了给 AMS 服务的,那 PMS 需要提供哪些数据呢?为什么需要 AndroidManifest.xml?

我们都知道 AndroidManifest.xml 定义了apk 中所有的四大组件、权限等等信息,它是一个定义文件。PMS 对 apk 的解析最主要的就是去扫描到 /data/app 和 /system/app 目录下的 apk 文件,找到 apk 包中的 AndroidManifest.xml,然后解析 AndroidManifest.xml 的信息保存到系统内存中,这样 AMS 在需要应用数据时,就能找到 PMS 快速的从内存中拿到相关信息。

如果没有 AndroidManifest.xml,PMS 的解析就是要保存每个 apk 中所有的类文件信息,这个数据量是庞大的,而且解析也会很慢,手机启动速度更慢。

PMS 的 apk 解析流程

PMS 的启动过程

在 Android 系统所有的核心服务都会经过 SystemServer 启动,PMS 也不例外,SystemServer 会在手机开机时启动运行。

SystemServer.javapublic static void main(String[] args) {new SystemServer().run();
}private void run() {...try {...startBootstrapServices();startCoreServices();startOtherServices();...}...
}private void startBootstrapServices() {...// 启动 AMS mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();mActivityManagerService.setSystemServiceManager(mSystemServiceManager);mActivityManagerService.setInstaller(installer);...// 启动 PMSmPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();   ... 
}PackageManagerService.javapublic static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// Self-check for initial settings.PackageManagerServiceCompilerMapping.checkProperties();// 创建自己的实例PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();// 将 PMS 添加到 ServiceManager,AMS 找 PMS 拿数据时就是通过 ServiceManager 找到 PMSServiceManager.addService("package", m); final PackageManagerNative pmn = m.new PackageManagerNative();ServiceManager.addService("package_native", pmn);return m;
}

当 SystemServer 被 Zygote 启动调用了 main() 方法时,执行了 SystemServer 的 run() 方法启动一些核心服务,例如先启动了 AMS 后再启动了 PMS,将 AMS 和 PMS 添加到 ServiceManager,由 ServiceManager 管理这些服务。

ServiceManager 只提供了 addService() 和 getService() 方法,当 app 进程需要获取到对应的系统服务,都会通过 ServiceManager 拿到相应服务的 Binder 代理,使用 Binder 通信获取数据:

例如在 Application、Activity 等地方调用 getPackageManager() 时:

ContextWrapper.java@Override
public PackageManager getPackageManager() {// mBase 是 ContextImplreturn mBase.getPackageManager();
}ContextImpl.java@Override
public PackageManager getPackageManager() {if (mPackageManager != null) {return mPackageManager;}IPackageManager pm = ActivityThread.getPackageManager();if (pm != null) {// Doesn't matter if we make more than one instance.return (mPackageManager = new ApplicationPackageManager(this, pm));}return null;
}ActivityThread.javapublic static IPackageManager getPackageManager() {if (sPackageManager != null) {//Slog.v("PackageManager", "returning cur default = " + sPackageManager);return sPackageManager;}// 通过 ServiceManager 拿到 PMSIBinder b = ServiceManager.getService("package");//Slog.v("PackageManager", "default service binder = " + b);sPackageManager = IPackageManager.Stub.asInterface(b); // binder 通信//Slog.v("PackageManager", "default service = " + sPackageManager);return sPackageManager;
}

PMS 解析 apk 流程

PMS 的处理流程简单理解就是手机开机时会去扫描两个目录 /data/app 和 /system/app,去解析这两个目录的 apk 文件的 AndroidManifest.xml 生成应用的摘要信息保存为 Java Bean 到内存。

PackageManagerService.java// data/app 目录
private static final File sAppInstallDir =new File(Environment.getDataDirectory(), "app");public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {...// /system/app 目录final File systemAppDir = new File(Environment.getRootDirectory(), "app");// 扫描 /system/app 目录下的 apk 文件scanDirTracedLI(systemAppDir,mDefParseFlags| PackageParser.PARSE_IS_SYSTEM_DIR,scanFlags| SCAN_AS_SYSTEM,0);	...// 扫描 /data/app 目录下的 apk 文件scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);...
}private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");try {scanDirLI(scanDir, parseFlags, scanFlags, currentTime);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}
}private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {final File[] files = scanDir.listFiles();...try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,mParallelPackageParserCallback)) {// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {// 判断是否是 .apk 后缀的文件final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}// 添加到子线程交给 PackageParser 解析 apk 文件parallelPackageParser.submit(file, parseFlags);fileCount++;}...}
}ParallelPackageParser.javapublic void submit(File scanFile, int parseFlags) {mService.submit(() -> {ParseResult pr = new ParseResult();Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");try {PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setDisplayMetrics(mMetrics);pp.setCacheDir(mCacheDir);pp.setCallback(mPackageParserCallback);pr.scanFile = scanFile; // 传入待解析的 apk 文件// 交给 packageParser 解析 apkpr.pkg = parsePackage(pp, scanFile, parseFlags);} catch (Throwable e) {pr.throwable = e;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}...});
}protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,int parseFlags) throws PackageParser.PackageParserException {return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}PackageParser.javapublic static final String APK_FILE_EXTENSION = ".apk";public static final boolean isApkFile(File file) {return isApkPath(file.getName());
}public static boolean isApkPath(String path) {return path.endsWith(APK_FILE_EXTENSION);
}

从源码可以看到,PMS 其实就是去扫描 /data/app/ 和 /system/app/ 两个目录下的 apk,判断目录下的文件是否是 apk 也只是简单的判断文件后缀是否是 .apk。然后通过 PackageParser 开始解析 apk。

需要注意的是,在不同的系统源码版本解析的方式也不相同,在 6.0、7.0、8.0 版本启动解析的方式还是直接解析的,但在 10.0 版本开始使用线程池放到子线程去解析,加快了手机启动速度。

PackageParser 类源码解析

根据上面的分析,apk 的解析最终是交给 PackageParser,继续查看是如何解析的:

PackageParser.javapublic Package parsePackage(File packageFile, int flags, boolean useCaches)throws PackageParserException {// 如果有缓存,直接返回解析后的信息Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;}        ...// apk 文件不是目录,所以会走的 parseMonolithicPackage()if (packageFile.isDirectory()) {parsed = parseClusterPackage(packageFile, flags);} else {parsed = parseMonolithicPackage(packageFile, flags);}...
}public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {...final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);try {// 解析 apkfinal Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);pkg.setCodePath(apkFile.getCanonicalPath());pkg.setUse32bitAbi(lite.use32bitAbi);return pkg;} catch (IOException e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to get path: " + apkFile, e);} finally {IoUtils.closeQuietly(assetLoader);}
}private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {final String apkPath = apkFile.getAbsolutePath();    ...// 开始 dom 解析 AndroidManifest.xmlXmlResourceParser parser = null;try {final int cookie = assets.findCookieForPath(apkPath);...// ANDROID_MANIFEST_FILENAME 就是 AndroidManifest.xmlparser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);...final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);...return pkg;} catch (PackageParserException e) {throw e;} catch (Exception e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to read manifest from " + apkPath, e);} finally {IoUtils.closeQuietly(parser);}
}private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,String[] outError) throws XmlPullParserException, IOException {final String splitName;final String pkgName;try {Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);pkgName = packageSplit.first; // 拿到包名splitName = packageSplit.second;...} ...// 后续的流程就是将 xml 解析的信息如权限、四大组件等信息存到 Packagefinal Package pkg = new Package(pkgName);...return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}public final static class Package implements Parcelable {// 包名public String packageName;...// 申请的权限public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);// 四大组件public final ArrayList<Activity> activities = new ArrayList<Activity>(0);public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);public final ArrayList<Provider> providers = new ArrayList<Provider>(0);public final ArrayList<Service> services = new ArrayList<Service>(0);...
}

上面的源码其实很好理解,就是根据传过来的 apk 文件路径先拿到 AndroidManifest.xml,然后开始进行 dom 解析 xml 文件,将不同的标签数据信息存放在 Package 类的不同字段,例如 权限信息、四大组件信息等,将它们都解析好存放到内存中,方便后续 AMS 找到 PMS 拿数据。

在 9.0 版本开始解析结果默认会开启缓存,如果有缓存则直接返回解析后的结果信息,否则就解析每个 apk 文件的 AndroidManifest.xml:

ParallelPackageParser.javaprotected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,int parseFlags) throws PackageParser.PackageParserException {// 开启缓存return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}PackageParser.javapublic Package parsePackage(File packageFile, int flags, boolean useCaches)throws PackageParserException {// 如果有缓存,直接返回Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;}...
}

以上就是 PMS 的 apk 解析流程,简单说就是提前将 AMS 要用的数据信息先解析存到内存,方便能快速定位到 Activity 等信息。

当我们在应用商店下载安装应用或使用 adb install 时也是走的上述的解析过程。

小结

再简单总结下 PMS 的 apk 解析流程:

  • 手机系统启动,Zygote 启动 SystemServer,SystemServer 启动 AMS、PMS,并注册到 ServiceManager
  • PMS 扫描 /data/app/ 和 /system/app/ 目录下的所有 apk 文件,获取每个 apk 文件的 AndroidManifest.xml 文件,并进行 dom 解析
  • 解析 AndroidManifest.xml 将权限、四大组件等数据信息转换为 Java Bean 记录到内存中
  • 当 AMS 需要获取 apk 数据信息时,通过 ServiceManager 获取到 PMS 的 Binder 代理通过 Binder 通信获取

知道 PMS 解析过程有什么作用?

了解了 PMS 解析 apk 的流程,我们可以根据原理 hook 实现动态装载的功能,使用 PackageParser 将网络下载的一个 apk 文件自己手动解析,然后通过反射添加到 PMS 的内存,实现动态装载功能。

下面的 demo 实现了一个简单的动态加载功能,将一个外部 apk 文件的广播添加到 PMS 的缓存中。

首先准备需要动态添加的广播,该广播放在外部 apk 文件 hook-debug.apk:

public class HookReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.i("HookReceiver", "hook receiver receive message");// 给宿主广播发消息Intent sendIntent = new Intent();sendIntent.setAction("com.example.demo.main");context.sendBroadcast(sendIntent);}
}<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.hook"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Demo" ><!-- 清单文件也需要添加广播注册,PackageParser 动态加载时需要使用 --><receiver android:name=".HookReceiver"android:exported="true"><intent-filter><action android:name="com.example.demo.hook" /></intent-filter></receiver></application>
</manifest>

将外部 apk 打包出来后,为了方便演示,demo 是将 apk 导入到 cache 目录。接下来是动态解析:

public class HookPackageParser {public void parse(Context context, File apkFile) throws Exception {Class<?> packageParserClazz = Class.forName("android.content.pm.PackageParser");Method parsePackageMethod = packageParserClazz.getDeclaredMethod("parsePackage", File.class, int.class);parsePackageMethod.setAccessible(true);Object packageParserObj = packageParserClazz.newInstance();// 调用 PackageParser.parsePackage() 获取到解析后的 PackageObject packageObj = parsePackageMethod.invoke(packageParserObj, apkFile, PackageManager.GET_RECEIVERS);// 获取 receivers 成员变量Field receiversField = packageObj.getClass().getDeclaredField("receivers");List receivers = (List) receiversField.get(packageObj);DexClassLoader dexClassLoader = new DexClassLoader(apkFile.getAbsolutePath(),context.getDir("plugin", Context.MODE_PRIVATE).getAbsolutePath(),null,context.getClassLoader());Class<?> componentClazz = Class.forName("android.content.pm.PackageParser$Component");Field intentsField = componentClazz.getDeclaredField("intents");for (Object receiverObj : receivers) {String name = (String) receiverObj.getClass().getField("className").get(receiverObj);try {BroadcastReceiver hookReceiver = (BroadcastReceiver) dexClassLoader.loadClass(name).newInstance();List<? extends IntentFilter> filters = (List<? extends IntentFilter>) intentsField.get(receiverObj);for (IntentFilter filter : filters) {context.registerReceiver(hookReceiver, filter);}} catch (Exception e) {// ignore}}}
}public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);checkPermission(this);// 注册一个用于接收 hook 广播发送的消息验证是否动态装载了外部 apk 的广播IntentFilter filter = new IntentFilter();filter.addAction("com.example.demo.main");registerReceiver(new MainReceiver(), filter);}private boolean checkPermission(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {activity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);}return false;}// 先 hook 外部 apk 的 HookReceiverpublic void hookReceiver(View view) {HookPackageParser packageParser = new HookPackageParser();File directory = getCacheDir();String path = directory.getAbsolutePath() + "/hook-debug.apk";File file = new File(path);if (!file.exists()) {throw new RuntimeException("hook apk no exist");}try {packageParser.parse(this, file);} catch (Exception e) {e.printStackTrace();}}// hook 后尝试发送广播看是否生效public void sendBroadcast(View view) {Intent intent = new Intent();intent.setAction("com.example.demo.hook");sendBroadcast(intent);}private static class MainReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.i("MainReceiver", "receive hook receiver message");}}
}

PackageParser 需要通过反射获取,再反射调用它的 parsePackage() 传入 apk 路径完成解析获取到 Package 对象,再反射 PMS 的 activities、providers、receivers、services 变量,将我们解析的数据添加进去,这样就实现了动态加载。

Android AMS(ActivityManagerService) 原理

什么是 AMS

AMS(ActivityManagerService)主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。通常情况下我们不会把 AMS 单独的拆分出来讲解,因为 AMS 需要通过 PMS(PackageManagerService)获取信息。

下面的节点在讲解 AMS 时会穿插 PMS(PackageManagerService) 相关的内容。

没有 PMS 和 AMS 会发生什么

在 Android PMS 原理 可以了解到,PMS 在手机开机的时候会运行解析所有 apk 的 AndroidManifest.xml,将每个 apk 信息存到内存中,并提供查询接口,可以认为 PMS 充当着包信息缓存的作用。

而 AMS 其中一个职责是管理调度 Activity,需要启动某个 Activity 时都会先找 PMS 查询要跳转的 Activity 信息再处理后续一系列的操作。

我们可以思考一个问题:为什么 Android 要提供 AMS 和 PMS?如果没有 AMS 和 PMS 会发生什么?

假设现在要启动某个进程的 Activity,简单梳理下会经历五个步骤:

  • 遍历 data/app 目录获取到该目录下所有 apk 文件
  • 解压所有 apk 获取 AndroidManifest.xml
  • dom 解析 AndroidManifest.xml 解析出 Activity 标签生成对应数据类存到内存
  • 从内存查找到要跳转的 Activity 信息,获取到 Activity 全类名,反射构建对象
  • 依次执行 Activity 的生命周期

如果每次进程调度都要这么处理,可以想象效率是很低的。

为了能快速的定位到要跳转的 Activity,前面的三个步骤将会交由 PMS,PMS 提前在开机启动时先解析完成存到内存,这样当 AMS 需要跳转某个 Activity 时,直接从 PMS 存储到内存的数据去提取信息,就能更快的完成操作。

AMS 从 PMS 拿到创建 Activity 的信息创建出来后,就会需要考虑 Activity 的生命周期,总不能 Activity 一退出就销毁,所以 AMS 就需要管理创建出来的 Activity 的生命周期。每个应用每个进程都有自己的 ActivityThread,所以 AMS 也需要一个缓存中心管理 Activity 的生命周期,就是由 ActivityThread 充当这个角色(更具体说是 ActivityThread 下的 mActivities 变量)。

ActivityThread.javafinal ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...Activity activity = null;try {// 创建 Activityjava.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);...} catch (Exception e) {...}...try {...if (activity != null) {...r.activity = activity; // ActivityClientRecord 记录 Activity}mActivities.put(r.token, r); // 将创建的 Activity 存储到 map} catch (SuperNotCalledException e) {...}...return activity;
}public static final class ActivityClientRecord {...Activity activity; // 持有 Activity...
}

可以看到 ActivityThread 的源码中有一个 mActivities 的成员变量,ActivityClientRecord 是持有 Activity 的引用。在后续的 performXxxActivity() 等处理生命周期的方法中都会用 mActivities 管理。

App 的启动过程

在 Android 无论是启动一个应用还是启动应用内的 Activity,都是调用的 startActivity() 方法。总体会经历三个步骤:

  • 告知 AMS 要启动一个指定的 Activity
  • AMS 从 PMS 查找要启动的 Activity 信息
  • 启动指定 Activity

接下来我们具体通过源码(API 28)分析 startActivity() 的整个过程。

Activity.java@Override
public void startActivity(Intent intent) {this.startActivity(intent, null);
}@Override
public void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {...// 委托给 InstrumentationInstrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);....}...
}

当我们调用 startActivity() 时,最终都会调用到 startActivityForResult(),应用上层对接 Android 核心服务会委托给 Instrumentation。Instrumentation 是专门负责 Application 和 Activity 的相关所有活动处理,就是和 AMS 通信会委托给 Instrumentation。

Instrumentation.javapublic ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {...try {...// 找到 AMS 告知要启动 Activityint result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);	...            	}...
}

Instrumentation 会告知 AMS 要启动一个 Activity。

需要注意的是,在这里不同系统版本的源码处理也不一样,在 9.0 之前是使用 AMS,9.0 之后是使用 ATMS(ActivityTaskManagerService):

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {...try {...// 9.0 之后会通过 ATMS 告知要启动 Activityint result = ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(tho.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);	...            	}...
}

目前源码是以 API 28 分析,所以我们回到 API 28 的源码分析。

ActivityManager.getService().startActivity() 这句代码可以拆分成两部分:ActivityManager.getService() 和 startActivity()。

我们先看 ActivityManager.getService():

ActivityManager.javapublic static IActivityManager getService() {return IActivityManagerSingleton.get();
}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {// 通过 ServiceManager 获取 AMS 服务// IActivityManager 是 binder 代理final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am; }};public abstract class Singleton<T> {private T mInstance;protected abstract T create();public final T get() {synchronized (this) {if (mInstance == null) {mInstance = create();}return mInstance;}}
}

ActivityManager.getService() 会获取 IActivityManager,它是一个 binder 代理对象。从代码可以看出,当使用 ActivityManager.getService() 对象调用方法时,实际上已经在做跨进程通信,由 binder 代理对象和 AMS 通信,通信所在的进程是 system_server。同时这里也是一个很好的 hook 点。

到这一步为止已经完成了告知 AMS 要启动 Activity 的第一个步骤。

我们继续分析源码。

ActivityManagerService.java@Override
public final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {// 有多个 startActivityAsUser() 重载方法,为了方便查看省略了多个方法调用  return startActivityAsUser(...); 
}public final int startActivityAsUser(...) {...// mActivityStartController.obtainStarter 返回 ActivityStarter 对象return mActivityStartController.obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setMayWait(userId).execute();
}

使用 AMS 调用 startActivity() 方法,主要是找到 mActivityStartController.obtainStarter() 获取到 ActivityStarter 对象,很明显使用了构建者模式配置相关参数,重点在 execute() 方法。

按照我们一开始设定的步骤,第二步是 AMS 要找到 PMS 获取跳转 Activity 相关的信息,那么 AMS 是怎么和 PMS 通信的?

ActivityStarter.javaint execute() {try {if (mRequest.mayWait) {return startActivityMayWait(...);} else {return startActivity(...);}} finally {...}
}private int startActivityMayWait(...) {...// AMS 和 PMS 产生关联,从 PMS 获取信息ResolveInfo rInfo = mSupervisor.resolveIntent(...);...return res;
}ActivityStackSupervisor.javaResolveInfo resolveIntent(...) {// mService 是 AMSsynchronized (mService) {...try {// mService.getPackageManagerInternalLocked() 返回 PackageManagerInternalImplreturn mService.getPackageManagerInternalLocked().resolveIntent(...);} finally {....}}...
}ActivityManagerService.javaPackageManagerInternal getPackageManagerInternalLocked() {if (mPackageManagerInt == null) {mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);}return mPackageManagerInt;
}PackageManagerService.java// PackageManagerInternalImpl 的作用是,它作为内部类能拿到外部类的引用
// 所以可以充当 AMS 和 PMS 的桥梁,让 AMS 能获取到 PMS
private class PackageManagerInternalImpl extends PackageManagerInternal {...@Overridepublic ResolveInfo resolveIntent(Intent intent, String resolvedType,int flags, int userId, boolean resolveForStart, int filterCallingUid) {// 作为桥梁外部调用相关方法时都转发给 PMS// ResolveInfo 持有 ActivityInfo、serviceInfo 等信息return resolveIntentInternal(intent, resolvedType, flags, userId, resolveForStart, filterCallingUid);}...
}ResolveInfo.javapublic class ResolveInfo implements Parcelable {public ActivityInfo activityInfo;public ServiceInfo serviceInfo;public ProviderInfo providerInfo;...
}

AMS 和 PMS 通信获取的是 ResolveInfo 包装对象,它可以存储 ActivityInfo、ServiceInfo 等信息,不直接返回 ActivityInfo 的原因应该是要启动 Service 等其他组件时可以复用同一套逻辑,因为获取的方式基本是相同的。

AMS 和 PMS 的通信也不是直接通信,而是通过 PMS 的 PackageManagerInternalImpl 内部类作为桥梁,内部类持有外部类的引用,所以 PackageManagerInternalImpl 可以直接访问 PMS。这样的做法既能实现功能,又能降低 AMS 和 PMS 之间的耦合,限制公开的 api 访问。

到这里已经完成第二步从 PMS 获取到要启动的 Activity 信息,就可以开始第三步启动 Activity。

ActivityStarter.javaprivate int startActivityMayWait(...) {...// AMS 和 PMS 产生关联,从 PMS 获取信息ResolveInfo rInfo = mSupervisor.resolveIntent(...);...// 从封装的 ResolveInfo 获取到要跳转的 ActivityInfo 信息ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);...// 开始启动 Activityint res = startActivity(...);...return res;
}private int startActivity(...) {int result = START_CANCELED;try {...result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity);} finally {...	}...
}private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity) {...mSuperVisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions);...
}ActivityStackSupervisor.javaboolean resumeFocusedStackTopActivityLocked(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {if (!readyToResume()) {return false;}if (targetStack != null && isFocusedStack(targetStack)) {return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}final ActivityRecord r = mFocusedStack.topRunningActivityLocked();if (r == null || !r.isState(RESUMED)) {mFocusedStack.resumeTopActivityUncheckedLocked(null, null);} else if (r.isState(RESUMED)) {// Kick off any lingering app transitions form the MoveTaskToFront operation.mFocusedStack.executeAppTransition(targetOptions);}return false;		
}ActivityStack.javaboolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {...try {...result = resumeTopActivityInnerLocked(prev, options);...}
}private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {...mStackSupervisor.startSpecificActivityLocked(next, true, false);...
}void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {...// 先判断要启动的 Activity 进程是否已经存在if (app != null && app.thread != null) {try {...// 进程已经存在,启动 ActivityrealStartActivityLocked(r, app, andResume, checkConfig);return;} catch (RemoteException e) {...}}// 如果进程不存在,AMS 通知 zygote 启动进程,最终反射调用 ActivityThread.main()// 有多个 startProcessLocked() 重载方法,为了方便查看省略了多个方法调用mService.startProcessLocked(...);	
}ActivityManagerService.javaprivate boolean startProcessLocked(...) {...// 其中一个 startProcessLocked() 提供了要启动的进程为 ActivityThreadfinal String entryPoint = "android.app.ActivityThread";... // 省略其他 startProcessLocked() 调用if (mConstants.FLAG_PROCESS_START_ASYNC) {...// 传入要启动的进程final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint, ...)}...
}private ProcessStartResult startProcess(String entryPoint, ...) {try {...startResult = Process.start(entryPoint, ...);...} finally {...}
}Process.javapublic static final ProcessStartResult start(final String processClass, ...) {return zygoteProcess.start(processClass, ...);
}

按上面源码的分析,启动 Activity 前其实还需要再细分拆成两个处理:

  • 要启动的 Activity 所在进程如果没有创建,AMS 会通知 Zygote fork 进程,最终会反射调用 ActivityThread 的 main() 方法,再走后续的 Activity 创建及后续生命周期流程

  • 进程已经创建,realStartActivityLocked() 创建 Activity 及后续生命周期流程

Activity 的启动涉及到了生命周期,AMS 既然是管理调度 Activity 的服务,那就需要能和启动的 Activity 有所关联。AMS 是用什么方式管理 Activity 的生命周期的?

ActivityThread.javafinal ApplicationThread mAppThread = new ApplicationThread();public static void main(String[] args) {...ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);...
}private void attach(boolean system, long startSeq) {...if (!system) {...final IActivityManager mgr = ActivityManager.getService();try {// 将 ApplicationThread 给到 AMS 作为句柄管理mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {...}...}...
}private class ApplicationThread extends IApplicationThread.Stub {// AMS 下发管理四大组件,将处理转发给变量名为 mH 的 Handler...@Overridepublic void scheduleTransaction(ClientTransaction transaction) {ActivityThread.this.scheduleTransaction(transaction);}
}class H extends Handler {// Activity 生命周期等消息的处理...
}

在 ActivityThread 的 main() 方法,可以从源码看到创建了一个 ApplicationThread,然后将这个对象给到了 AMS,Activity 生命周期其实就是使用的 ApplicationThread 作为句柄交给 AMS,AMS 就可以通过这个句柄下发管理 Activity 的生命周期;同样的 AMS 也通过 ApplicationThread 管理四大组件和进程的其他处理,在 ApplicationThread 由 Handler 转发消息驱动处理。

启动 Activity 之前,还需要先启动创建 Application。AMS 获取 ApplicationThread 调用了 attachApplication():

ActivityManagerService.javapublic class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {@Overridepublic final void attachApplication(IApplicationThread thread, long startSeq) {synchronized (this) {...attachApplicationLocked(thread, callingPid, callingUid, startSeq);...}}@GuardedBy("this")private final boolean attachApplicationLocked(IApplicationThread thread,int pid, int callingUid, long startSeq) {...if (app.isolatedEntryPoint != null) {...} else if (app.instr != null) {// 通过 ApplicationThread 和 App 进程通信thread.bindApplication(processName, appInfo, providers,app.instr.mClass,profilerInfo, app.instr.mArguments,app.instr.mWatcher,app.instr.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(getGlobalConfiguration()), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, isAutofillCompatEnabled);			} else {...	}...if (normalMode) {try {// 创建完 Application 后走 Activity 生命周期流程if (mStackSupervisor.attachApplicationLocked(app)) {didSomething = true;}} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}}    
}ActivityThread.javaprivate class ApplicationThread extends IApplicationThread.Stub {public final void bindApplication(...) {...// 在 ActivityThread 也是获取的 AMS 数据创建 applicationAppBindData data = new AppBindData();data.processName = processName;data.appInfo = appInfo;data.providers = providers;data.instrumentationName = instrumentationName;data.instrumentationArgs = instrumentationArgs;data.instrumentationWatcher = instrumentationWatcher;data.instrumentationUiAutomationConnection = instrumentationUiConnection;data.debugMode = debugMode;data.enableBinderTracking = enableBinderTracking;data.trackAllocation = trackAllocation;data.restrictedBackupMode = isRestrictedBackupMode;data.persistent = persistent;data.config = config;data.compatInfo = compatInfo;data.initProfilerInfo = profilerInfo;data.buildSerial = buildSerial;data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;sendMessage(H.BIND_APPLICATION, data); // 消息驱动发消息给到 Handler}
}class H extends Handler {public static final int BIND_APPLICATION        = 110;...public void handleMessage(Message msg) {switch (mssg.what) {case BIND_APPLICATION:AppBindData data = (AppBindData) msg.obj;handleBindApplication(data);break;...}}	
}private void handleBindApplication(AppBindData data) {...// 反射创建 Instrumentation 负责管理 application 和 activity 相关所有活动处理try {final ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();} catch (Exception e) {...}...try {// info 是 LoadedApk,反射创建 application app = data.info.makeApplication(data.restrictedBackupMode, null);...}...
} public final class LoadedApk {public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {...// 创建 applicationApplication app = null;try {ClassLoader cl = getClassLoader();ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);} catch (Exception e) {...}...if (instrumentation != null) {try {// 调用 application.onCreate()instrumentation.callApplicationOnCreate(app);} catch (Exception e) {...	}}...}	
}

attachApplication() 实际上做了三件事情:

  • 创建 Instrumentation,负责管理 Application 和 Activity 相关所有活动处理
  • 创建 Application
  • 创建 Activity 开始走生命周期流程

在上面我们有提到,Activity 的创建和后续生命周期流程是从 realStartActivityLocked() 方法开始的。让我们继续分析 Activity 的创建过程。

ActivityManagerService.javapublic class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {final ActivityStackSupervisor mStackSupervisor;    @Overridepublic final void attachApplication(IApplicationThread thread, long startSeq) {synchronized (this) {...attachApplicationLocked(thread, callingPid, callingUid, startSeq);...}}@GuardedBy("this")private final boolean attachApplicationLocked(IApplicationThread thread,int pid, int callingUid, long startSeq) {...if (mStackSupervisor.attachApplicationLocked(app)) {}...}    
}ActivityStackSupervisor.javapublic class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,RecentTasks.Callbacks {boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {...if (realStartActivityLocked(activity, app, top == activity) {}...}	final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,boolean andResume, boolean checkConfig) throws RemoteException {...// app.thread 是 ApplicationThread// Create activity launch transaction.final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,r.appToken);clientTransaction.addCallback(LaunchActivityItem.obtain(...));	final ActivityLifecycleItem lifecycleItem;if (andResume) {lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());} else {lifecycleItem = PauseActivityItem.obtain();}  // 执行完 LaunchActivityItem 事务后要处在的生命周期状态   				clientTransaction.setLifecycleStateRequest(lifecycleItem);// mService 是 AMS// Schedule transaction.mService.getLifecycleManager().scheduleTransaction(clientTransaction);...}	
}

ClientTransaction 是客户端事务管理类,通过不同的状态 LaunchActivityItem、ResumeActivityItem、PauseActivityItem 等,分别代表不同的 Activity 生命周期,以状态的方式加以管理。

在上面有提到 AMS 管理 Activity 的生命周期是通过 ApplicationThread 句柄,在创建 ClientTransaction.obtain() 也能看到是传入了 ApplicationThread 下发的生命周期处理:

ClientTransaction.javapublic class ClientTransaction implements Parcelable, ObjectPoolItem {private IApplicationThread mClient;// 记录事务结束后要处在的生命周期状态,后面会用到public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {mLifecycleStateRequest = stateRequest;}public void schedule() throws RemoteException {mClient.scheduleTransaction(this);}public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);if (instance == null) {instance = new ClientTransaction();}instance.mClient = client; // 存储的 ApplicationThreadinstance.mActivityToken = activityToken;return instance;}	
}

继续分析 mService.getLifecycleManager().scheduleTransaction(clientTransaction) 做了什么事情:

ActivityManagerService.javapublic class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {ClientLifecycleManager getLifecycleManager() { return mLifecycleManager; }
}class ClientLifecycleManager {void scheduleTransaction(ClientTransaction transaction) throws RemoteException {final IApplicationThread client = transaction.getClient();transaction.schedule();...}
}ClientTransaction.javapublic class ClientTransaction implements Parcelable, ObjectPoolItem {private IApplicationThread mClient;public void schedule() throws RemoteException {mClient.scheduleTransaction(this); // 拿着 ApplicationThread 下发通知}	
}ActivityThread.javaprivate class ApplicationThread extends IApplicationThread.Stub {@Overridepublic void scheduleTransaction(ClientTransaction transaction) throws RemoteException {// 在 ActivityThread 是找不到 scheduleTransaction 方法的// 而是在 ActivityThread 的父类 ClientTransactionHandler 调用ActivityThread.this.scheduleTransaction(transaction);}	
}

AMS 就是拿着 ApplicationThread 句柄告知 ActivityThread 要创建 Activity,在 ActivityThread 的源码中你会发现 ActivityThread.this.scheduleTransaction() 代码没有找到,实际上它是放在了 ActivityThread 继承的父类 ClientTransactionHandler。

ClientTransactionHandler.javapublic abstract class ClientTransactionHandler {void scheduleTransaction(ClientTransaction transaction) {transaction.preExecute(this);// 通过 Handler 发了一条消息sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);}
}public final class ActivityThread extends ClientTransactionHandler {// TransactionExecutor的构造传入的 ActivityThreadprivate final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);	class H extends Handler {public static final int EXECUTE_TRANSACTION = 159;public void handleMessage(Message msg) {switch (msg.what) {case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);break;}}}
}TransactionExecutor.javapublic class TransactionExecutor {// ActivityThreadprivate ClientTransactionHandler mTransactionHandler;public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {mTransactionHandler = clientTransactionHandler;}public void execute(ClientTransaction transaction) {...executeCallbacks(transaction); executeLifecycleState(transaction);...	}public void executeCallbacks(ClientTransaction transaction) {final List<ClientTransactionItem> callbacks = transaction.getCallbacks();...final int size = callbacks.size();for (int i = 0; i < size; ++i) {final ClientTransactionItem item = callbacks.get(i);...// 执行事务,这里是设置了 LaunchActivityItemitem.execute(mTransactionHandler, token, mPendingActions);...}}private void executeLifecycleState(ClientTransaction transaction) {final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();...// 执行完事务后处在的生命周期状态,这里是 ResumeActivityItemlifecycleItem.execute(mTransactionHandler, token, mPendingActions);}
}

在 Handler 接收到消息后启动事务的处理,处理事物交由 TransactionExecutor 负责,主要看两个函数:

  • executeCallbacks():执行具体的事务,例如 LaunchActivityItem
  • executeLifecycleState():执行事务后要处在哪个状态,例如 ResumeActivityItem、PauseActivityItem
LaunchActivityItem.javapublic class LaunchActivityItem extends ClientTransactionItem {@Overridepublic void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mIsForward,mProfilerInfo, client);// 最终到 ActivityThread 开始创建 Activityclient.handleLaunchActivity(r, pendingActions, null /* customIntent */);		}
}ActivityThread.javapublic final class ActivityThread extends ClientTransactionHandler {@Overridepublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {...final Activity a = performLaunchActivity(r, customIntent);...}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...// 通过 Instrumentation 反射创建 ActivityContextImpl appContext = createBaseContextForActivity(r);Activity activity = null;try {ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);...}...// 调用 Activity 生命周期 onCreate()if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}		}
}

简单总结下 realStartActivityLocked() 做了什么事情:

创建了 ClientTransaction 处理生命周期的执行事务,可以认为它是 AMS 具体处理 Activity 生命周期的执行类。不同的生命周期用不同的状态表示, LaunchActivityItem 表示的 onCreate() 的生命周期,还有 ResumeActivityItem 表示的 onResume() 的生命周期,事务具体执行最终都会下发到对应的 ActivityLifecycleItem,由这些状态类执行回调 Activity 的生命周期。

至此,App 的启动流程就分析到这里。

总结下整体 App 的启动过程:

  • 调用 startActivity() 时,实际会走到 Instrumentation,由它与 AMS 通信
  • Instrumentation 会找 ServiceManager 获取 AMS(实际是获取 binder 代理)调用 startActivity()。在 9.0 之前是获取 AMS,9.0 之后是获取 ATMS
  • AMS 找到 PMS 获取启动的 Activity 信息
  • 然后判断需要启动的 Activity 所在进程是否已存在,不存在 AMS 通过 socket 通知 Zygote fork 进程,然后反射调用 ActivityThread 的 main(),创建 Instrumentation、Application 和 Activity 以及走生命周期流程
  • 如果需要启动的 Activity 所在进程已经存在,创建 Activity 以及走生命周期流程

具体流程如下:

hook 启动未在 AndroidManifest.xml 注册的界面

有关 AMS 的源码已经分析完毕,那么知道 AMS 的源码有哪些应用场景呢?

我们经常会在业务中遇到一种场景,如果用户未登陆就跳转到登陆界面,已登陆就跳转到其他界面,所以也就会出现类似这种写法:

if (isLogin) {startActivity(new Intent(context, LoginActivity.class);
} else {startActivity(new Intent(context, OtherActivity.class);
}

当项目有大量的地方需要判断登陆再跳转界面,后续如果有业务改动,比如跳转的登陆界面修改,这种硬编码的方式侵入性高,要修改的范围很广,并不利于维护。

或许你会说:我用隐式意图在 AndroidManifest.xml 定义 action,然后用常量类提供 action 统一管理不就行了:

AndroidManifest.xml<manifest><application><activity name="LoginActivity"><intent-filter><action name="android.intent.action.login" /></intent-filter></activity>	</application>
</manifest>public class Constants {public static final String ACTION_LOGIN = "android.intent.action.login";
}if (isLogin) {startActivity(new Intent(context, Constants.ACTION_LOGIN);
} else {startActivity(new Intent(context, OtherActivity.class);
}

那我再加一个条件:我想不在 AndroidManifest.xml 注册登陆界面想正常启动,能做到吗?

正常情况是不行的,但我们在熟悉了 AMS 和 PMS 的源码后,就能通过 hook 绕过系统检测正常启动一个 Activity。

hook 简单来说就是绕过系统处理,自己用不同的方式实现同样的效果,但这个过程还是要用到系统创建的一些信息帮助我们完成处理。

但是要使用 hook 一般有三个前提条件:

  • 找到合适的 hook 点:在 Java 中一般是静态成员变量或成员方法,非静态成员变量是不能 hook 的,因为会借助反射获取 hook 点
  • hook 的兼容性:hook 会借助系统源码的 api,但不同版本的源码会存在不同的 hook 点,hook 方式也不一样
  • 熟悉源码原理:保证系统流程正常情况能正确 hook 处理

根据一开始提到的每次跳转都要鉴权的案例,我们尝试用 hook 实现只在代码写具体业务跳转,自动完成没有登陆就跳转登陆界面,否则就跳转具体业务界面。demo 将在 API 28 的源码上实现。

要实现这个效果有两大难点:

  • 不在 AndroidManifest.xml 注册界面,怎么绕过检查?
  • 绕过检查后,又该怎么正常启动这个 Activity?

不在 AndroidManifest.xml 注册界面,在文章最开始有提到 AMS 的原理分析是离不开 PMS的,因为 AMS 在启动 Activity 过程中会找 PMS 拿 Activity 的信息,所以这个问题更具体说是如何绕过 PMS 的检查。

为了实现这个效果,hook 能做的就是将 startActivity() 拦截下来,替换掉携带着目标 Activity 的 Intent 信息,在原始数据保留的情况下增加额外的信息到 Intent。这样也就能在 PMS 的检查下流程正常执行。

那么合适的 hook 在哪里呢?Instrumentation 有一个和 AMS 通信的处理,我们要拿到 AMS 的 binder 代理,这里重新将源码贴出来:

ActivityManager.java// 已经创建好的 IActivityManager,更具体说要拿到 mInstance,是一个很好的 hook 点
public static IActivityManager getService() {return IActivityManagerSingleton.get();
}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am; }};public abstract class Singleton<T> {private T mInstance;protected abstract T create();public final T get() {synchronized (this) {if (mInstance == null) {mInstance = create();}return mInstance;}}
}

hook 并不是完全要自己创建信息,而是要利用系统 api 或提前创建好的信息加以利用,mInstance 即 IActivityManager 是我们要用到的对象,要拦截它调用 startActivity() 时的处理,替换我们的 Intent,这需要用到动态代理。代码如下:

public class Hooker {public void hook() throws Exception {hookAms();}public void hookAms() throws Exception {if (proxyActivity == null) {throw new NullPointerException("proxyActivity is null");}Class ActivityManagerClz = Class.forName("android.app.ActivityManager");Field IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");IActivityManagerSingletonField.setAccessible(true);// 获取到 ActivityManager 的 IActivityManagerSingleton 内部静态成员变量Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);Class SingletonClz = Class.forName("android.util.Singleton");Field mInstanceField = SingletonClz.getDeclaredField("mInstance");mInstanceField.setAccessible(true);// 拿到 ActivityManagerService 的 binder 代理// 做这一步是为了绕过 AMS 的鉴权,因为要设置的 LoginActivity 也是没有在 AndroidManifest.xml 注册,也就是要绕过 PMSObject IActivityManagerObj = mInstanceField.get(IActivityManagerSingletonObj);Class IActivityManagerClz = Class.forName("android.app.IActivityManager");Object proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[] {IActivityManagerClz}, new AmsInvocationHandler(IActivityManagerObj));// 将 IActivityManagerSingleton 的 mInstance 成员替换为我们自己的代理对象// 将 mInstance 的调用都跑我们的代理mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);}private class AmsInvocationHandler implements InvocationHandler {private final Object iActivityManagerObject;public AmsInvocationHandler(Object iActivityManagerObject) {this.iActivityManagerObject = iActivityManagerObject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 系统使用 mInstance 的调用都会走到代理if ("startActivity".contains(method.getName())) {Intent intent = null;int index = 0;for (int i = 0; i < args.length; i++) {Object arg = args[i];if (arg instanceof Intent) {intent = (Intent) args[i];index = i;break;}}if (intent != null) {Intent proxyIntent = new Intent();ComponentName componentName = new ComponentName(context, proxyActivity);proxyIntent.setComponent(componentName);proxyIntent.putExtra("oldIntent", intent);args[index] = proxyIntent; // 替换真实意图}}return method.invoke(iActivityManagerObject, args);}}
}

绕过 PMS 的问题已经解决,接下来是第二个问题:怎么正常启动 Activity?

Activity 的创建和生命周期流程都是在 Handler 消息驱动下完成的,定位到具体源码是 ActivityThread 的 mH 成员变量:

ActivityThread.javafinal H mH = new H();class H extends Handler {public static final int EXECUTE_TRANSACTION = 159;public void handleMessage(Message msg) {switch (msg.what) {case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);break;}}
}

很遗憾的是,不能在 ActivityThread 将 handleMessage() 的消息拦截下来。

实际上 Handler 已经为我们提供了 hook 点:

Handler.javafinal Callback mCallback;public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {// 如果 mCallback 不为空,先处理 mCallback 的 handleMessage()// 如果 mCallback 的 handleMessage() 返回 false,调用兜底的 handleMessage()if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

可以设置我们自己的 mCallback,这样就能在兜底的 handleMessage() 之前,提前将消息拦截处理,需要拦截的消息返回 true 不再传递,否则返回 false 按正常走不影响启动流程。

完整代码如下:

public class Hooker {private final Context context;private Class<?> proxyActivity;public Hooker(@NonNull Context context) {this.context = context;}public void setProxyActivity(@NonNull Class<?> proxyActivity) {this.proxyActivity = proxyActivity;}public void hook() throws Exception {hookAms();hookSystemHandler();}public void hookAms() throws Exception {if (proxyActivity == null) {throw new NullPointerException("proxyActivity is null");}Class ActivityManagerClz = Class.forName("android.app.ActivityManager");Field IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");IActivityManagerSingletonField.setAccessible(true);// 获取到 ActivityManager 的 IActivityManagerSingleton 内部静态成员变量Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);Class SingletonClz = Class.forName("android.util.Singleton");Field mInstanceField = SingletonClz.getDeclaredField("mInstance");mInstanceField.setAccessible(true);// 拿到 ActivityManagerService 的 binder 代理// 做这一步是为了绕过 AMS 的鉴权,因为要设置的 LoginActivity 也是没有在 AndroidManifest.xml 注册,也就是要绕过 PMSObject IActivityManagerObj = mInstanceField.get(IActivityManagerSingletonObj);Class IActivityManagerClz = Class.forName("android.app.IActivityManager");Object proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[] {IActivityManagerClz}, new AmsInvocationHandler(IActivityManagerObj));// 将 IActivityManagerSingleton 的 mInstance 成员替换为我们自己的代理对象// 将 mInstance 的调用都跑我们的代理mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);}public void hookSystemHandler() throws Exception {Class ActivityThreadClz = Class.forName("android.app.ActivityThread");Field field = ActivityThreadClz.getDeclaredField("sCurrentActivityThread");field.setAccessible(true);Object ActivityThreadObj = field.get(null);Field mHField = ActivityThreadClz.getDeclaredField("mH");mHField.setAccessible(true);Handler mHObj = (Handler) mHField.get(ActivityThreadObj);Field mCallbackField = Handler.class.getDeclaredField("mCallback");mCallbackField.setAccessible(true);ProxyHandlerCallback proxyCallback = new ProxyHandlerCallback();mCallbackField.set(mHObj, proxyCallback);}private class ProxyHandlerCallback implements Handler.Callback {public static final int EXECUTE_TRANSACTION = 159;@Overridepublic boolean handleMessage(Message msg) {if (msg.what == EXECUTE_TRANSACTION) {try {Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");if (!ClientTransactionClz.isInstance(msg.obj)) {return false;}Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");mActivityCallbacksField.setAccessible(true);Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);List list = (List) mActivityCallbacksObj;if (list.size() == 0) {return false;}Object LaunchActivityItemObj = list.get(0);if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) {return false;}Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");mIntentField.setAccessible(true);Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);Intent realIntent = mIntent.getParcelableExtra("oldIntent");if (realIntent != null) {SharedPreferences sp = context.getSharedPreferences("name", MODE_PRIVATE);boolean isLogin = sp.getBoolean("isLogin", false);if (isLogin) {mIntent.setComponent(realIntent.getComponent());} else {ComponentName componentName = new ComponentName(context, LoginActivity.class);mIntent.putExtra("extraIntent", realIntent.getComponent().getClassName()); // 提供给登陆界面登陆成功后跳转到哪个目标页面mIntent.setComponent(componentName);}}} catch (Exception e) {e.printStackTrace();}}return false; // 返回 false 不影响系统的执行}}private class AmsInvocationHandler implements InvocationHandler {private final Object iActivityManagerObject;public AmsInvocationHandler(Object iActivityManagerObject) {this.iActivityManagerObject = iActivityManagerObject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 系统使用 mInstance 的调用都会走到代理if ("startActivity".contains(method.getName())) {Intent intent = null;int index = 0;for (int i = 0; i < args.length; i++) {Object arg = args[i];if (arg instanceof Intent) {intent = (Intent) args[i];index = i;break;}}if (intent != null) {Intent proxyIntent = new Intent();ComponentName componentName = new ComponentName(context, proxyActivity);proxyIntent.setComponent(componentName);proxyIntent.putExtra("oldIntent", intent);args[index] = proxyIntent; // 替换真实意图}}return method.invoke(iActivityManagerObject, args);}}
}

这样就能完成我们的效果了,写个 demo 测试下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.demo"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Demo"><activity android:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><!-- 没有注册 LoginActivity --><activity android:name=".Page1Activity" /><activity android:name=".Page2Activity" /></application>
</manifest>public class MainActivity extends AppCompatActivity {private Hooker hooker;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);hooker = new Hooker(this);}public void logout(View view) {SharedPreferences sp = getSharedPreferences("name", MODE_PRIVATE);sp.edit().putBoolean("isLogin", false).apply();}public void startPage1(View view) {hooker.setProxyActivity(Page1Activity.class);try {hooker.hook();} catch (Exception e) {e.printStackTrace();}startActivity(new Intent(this, Page1Activity.class));}public void startPage2(View view) {hooker.setProxyActivity(Page2Activity.class);try {hooker.hook();} catch (Exception e) {e.printStackTrace();}startActivity(new Intent(this, Page2Activity.class));}
}public class LoginActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);}public void login(View view) throws ClassNotFoundException {SharedPreferences sp = getSharedPreferences("name", MODE_PRIVATE);sp.edit().putBoolean("isLogin", true).apply();String className = getIntent().getStringExtra("extraIntent");if (!TextUtils.isEmpty(className)) {startActivity(new Intent(this, Class.forName(className)));finish();}}
}


http://www.ppmy.cn/news/1102724.html

相关文章

[EROOR] SpringMVC之500 回调函数报错

首先&#xff0c;检查一下idea里面的报错的原因&#xff0c;我的是jdk的版本的问题。所以更换一下就可以了。

数据库选择题笔记

索引&#xff1a;关系数据库中对某一列或多个列的值进行预排序的数据结构唯一索引&#xff1a;在设计关系数据表的时候&#xff0c;看上去唯一的列&#xff0c;例如身份证号、邮箱地址等&#xff0c;因为他们具有业务含义&#xff0c;因此不宜作为主键。 但是&#xff0c;这些列…

中国地图坐标系转换详解:从WGS-84到GCJ-02再到BD-09

目录 中国地图坐标系转换详解:从WGS-84到GCJ-02再到BD-09引言坐标系简介WGS-84GCJ-02BD-09转换算法实现WGS84和火星坐标(GCJ02)互转火星坐标(GCJ02)和百度坐标(BD09)互转测试方法仓库代码地址中国地图坐标系转换详解:从WGS-84到GCJ-02再到BD-09 引言 在地理信息系统(GIS)…

TortoiseGit设置作者信息和用户名、密码存储

前言 Git 客户端每次与服务器交互&#xff0c;都需要输入密码&#xff0c;但是我们可以配置保存密码&#xff0c;只需要输入一次&#xff0c;就不再需要输入密码。 操作说明 在任意文件夹下&#xff0c;空白处&#xff0c;鼠标右键点击 在弹出菜单中按照下图点击 依次点击下…

表情识别-情感分析-人脸识别(代码+教程)

表情识别 面部情绪识别&#xff08;FER&#xff09;是指根据面部表情识别和分类人类情绪的过程。通过分析面部特征和模式&#xff0c;机器可以对一个人的情绪状态作出有根据的推断。这个面部识别的子领域高度跨学科&#xff0c;涉及计算机视觉、机器学习和心理学等领域的知识。…

前端选择器:掌握未来Web开发的关键技术

引言 随着Web技术的不断发展&#xff0c;前端选择器已经成为现代Web开发中不可或缺的一部分。它们允许开发者以更加灵活和高效的方式操作DOM&#xff08;文档对象模型&#xff09;&#xff0c;从而实现复杂的交互效果。在本文中&#xff0c;我们将深入探讨前端选择器的各种类型…

Java输入-a,-b,geek,-c,888,-d,[hello,world]字符之后,如何将[hello,world]这个不分开

Java输入-a,-b,geek,-c,888,-d,[hello,world]字符之后&#xff0c;如何将[hello,world]这个不分开&#xff1f; 你可以使用命令行参数解析库来处理Java输入中的各个参数。在这种情况下&#xff0c;你可以使用Apache Commons CLI库来解析命令行参数。以下是一个示例代码片段&am…

Linux 中的 chown 命令及示例

操作系统中的不同用户拥有所有权和权限,以确保文件的安全并限制谁可以修改文件的内容。在Linux中,有不同的用户使用系统: Root用户: 它是超级用户,可以访问我们系统中的所有目录和文件,并且可以执行任何操作。需要注意的重要一点是,只有 root 用户可以更改不属于他们的…