Android应用程序进程启动过程(后篇)

本文首发于微信公众号「后厂技术官」

前言

在前篇中我们讲到了Android应用程序进程启动过程,这一篇我们来讲遗留的知识点:在应用程序进程创建过程中会启动Binder线程池以及在应用程序进程启动后会创建消息循环。

<!--more-->

1.Binder线程池启动过程

我们首先来看RuntimeInit类的zygoteInit函数,如下所示 frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

1 public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) 2 throws ZygoteInit.MethodAndArgsCaller { 3 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); 4 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit"); 5 redirectLogStreams(); 6 commonInit(); 7 nativeZygoteInit();//1 8 applicationInit(targetSdkVersion, argv, classLoader); 9 }

注释1处会在新创建的应用程序进程中创建Binder线程池,来查看nativeZygoteInit函数:

1 private static final native void nativeZygoteInit();

很明显nativeZygoteInit是一个jni方法,它对应的函数是什么呢。在 AndroidRuntime.cpp的JNINativeMethod数组中我们得知它对应的函数是com_android_internal_os_RuntimeInit_nativeZygoteInit,如下所示。 frameworks/base/core/jni/AndroidRuntime.cpp

1static const JNINativeMethod gMethods[] = { 2 { "nativeFinishInit", "()V", 3 (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, 4 { "nativeZygoteInit", "()V", 5 (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit }, 6 { "nativeSetExitWithoutCleanup", "(Z)V", 7 (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, 8};

接着来查看 com_android_internal_os_RuntimeInit_nativeZygoteInit函数: frameworks/base/core/jni/AndroidRuntime.cpp

1static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz) 2{ 3 gCurRuntime->onZygoteInit(); 4}

gCurRuntime是在AndroidRuntime初始化就创建的。如下所示。 frameworks/base/core/jni/AndroidRuntime.cpp

1AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) : 2 mExitWithoutCleanup(false), 3 mArgBlockStart(argBlockStart), 4 mArgBlockLength(argBlockLength) 5{ 6 ... 7 gCurRuntime = this; 8}

Android系统启动流程(二)解析Zygote进程启动过程这篇文章我们得知AppRuntime继承AndroidRuntime,AppRuntime创建时就会调用AndroidRuntime的构造函数,gCurRuntime就会被初始化,它指向的是AppRuntime,因此我们来查看AppRuntime的onZygoteInit函数,AppRuntime的实现在app_main.cpp中,如下所示。 frameworks/base/cmds/app_process/app_main.cpp

1 virtual void onZygoteInit() 2 { 3 sp<ProcessState> proc = ProcessState::self(); 4 ALOGV("App process: starting thread pool.\n"); 5 proc->startThreadPool(); 6 }

最后一行会调用ProcessState的startThreadPool函数: frameworks/native/libs/binder/ProcessState.cpp

1void ProcessState::startThreadPool() 2{ 3 AutoMutex _l(mLock); 4 if (!mThreadPoolStarted) { 5 mThreadPoolStarted = true; 6 spawnPooledThread(true); 7 } 8}

支持Binder通信的进程中都有一个ProcessState类,它里面有一个mThreadPoolStarted 变量,来表示Binder线程池是否已经被启动过,默认值为false。在每次调用这个函数时都会先去检查这个标记,从而确保Binder线程池只会被启动一次。如果Binder线程池未被启动则设置mThreadPoolStarted为true,最后调用spawnPooledThread函数来创建线程池中的第一个线程,也就是线程池的main线程,如下所示。 frameworks/native/libs/binder/ProcessState.cpp

1void ProcessState::spawnPooledThread(bool isMain) 2{ 3 if (mThreadPoolStarted) { 4 String8 name = makeBinderThreadName(); 5 ALOGV("Spawning new pooled thread, name=%s\n", name.string()); 6 sp<Thread> t = new PoolThread(isMain); 7 t->run(name.string());//1 8 } 9}

可以看到Binder线程为一个PoolThread。注释1调用PoolThread的run函数来启动一个启动一个新的线程。来查看PoolThread类里做了什么: frameworks/native/libs/binder/ProcessState.cpp

1class PoolThread : public Thread 2{ 3.. 4protected: 5 virtual bool threadLoop() 6 { 7 IPCThreadState::self()->joinThreadPool(mIsMain);//1 8 return false; 9 } 10 const bool mIsMain; 11};

PoolThread类继承了Thread类。注释1处会将调用IPCThreadState的joinThreadPool函数,将当前线程注册到Binder驱动程序中,这样我们创建的线程就加入了Binder线程池中,这样新创建的应用程序进程就支持Binder进程间通信了,Binder线程池启动过程就讲到这,接下来我们来学习消息循环创建过程。

2.消息循环创建过程

首先我们回到上篇最后讲到的RuntimeInit的invokeStaticMain函数,代码如下所示。 frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

1private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) 2 throws ZygoteInit.MethodAndArgsCaller { 3 Class<?> cl; 4 ... 5 throw new ZygoteInit.MethodAndArgsCaller(m, argv); 6}

invokeStaticMain函数在上篇已经讲过,这里不再赘述,主要是看最后一行,会抛出一个MethodAndArgsCaller异常,这个异常会被ZygoteInit的main函数捕获,如下所示。 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

1 public static void main(String argv[]) { 2 ... 3 try { 4 ... 5 } catch (MethodAndArgsCaller caller) { 6 caller.run();//1 7 } catch (RuntimeException ex) { 8 Log.e(TAG, "Zygote died with exception", ex); 9 closeServerSocket(); 10 throw ex; 11 } 12 }

注释1处捕获到MethodAndArgsCaller 时会执行caller的run函数,如下所示。 frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

1 public static class MethodAndArgsCaller extends Exception 2 implements Runnable { 3 private final Method mMethod; 4 private final String[] mArgs; 5 public MethodAndArgsCaller(Method method, String[] args) { 6 mMethod = method; 7 mArgs = args; 8 } 9 public void run() { 10 try { 11 mMethod.invoke(null, new Object[] { mArgs });//1 12 } catch (IllegalAccessException ex) { 13 throw new RuntimeException(ex); 14 } 15 ... 16 throw new RuntimeException(ex); 17 } 18 } 19 }

根据上一篇文章我们得知,mMethod指的就是ActivityThread的main函数,mArgs 指的是应用程序进程的启动参数。在注释1处调用ActivityThread的main函数,代码如下所示。 frameworks/base/core/java/android/app/ActivityThread.java

1 public static void main(String[] args) { 2 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); 3 SamplingProfilerIntegration.start(); 4... 5 Looper.prepareMainLooper();//1 6 ActivityThread thread = new ActivityThread();//2 7 thread.attach(false); 8 if (sMainThreadHandler == null) { 9 sMainThreadHandler = thread.getHandler(); 10 } 11 if (false) { 12 Looper.myLooper().setMessageLogging(new 13 LogPrinter(Log.DEBUG, "ActivityThread")); 14 } 15 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 16 Looper.loop();//3 17 throw new RuntimeException("Main thread loop unexpectedly exited"); 18 }

注释1处在当前应用程序进程中创建消息循环,注释2处创建ActivityThread,注释3处调用Looper的loop,使得Looper开始工作,开始处理消息。可以看出,系统在应用程序进程启动完成后,就会创建一个消息循环,用来方便的使用Android的消息处理机制。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )