注册Activity应用进程到system_server以及创建Activity应用进程Application

Android四大组件源码实现详解系列博客目录:

Android应用进程创建流程大揭秘
[Android四大组件之bindService源码实现详解
Android四大组件之Activity启动流程源码实现详解概要
Activity启动流程(一)发起端进程请求启动目标Activity
Activity启动流程(二)system_server进程处理启动Activity请求
Activity启动流程(三)-Activity Task调度算法复盘分析
Activity启动流程(四)-Pause前台显示Activity,Resume目标Activity
Activity启动流程(五)请求并创建目标Activity进程
Activity启动流程(六)注册目标Activity进程到system_server进程以及创建目标Activity进程Application
Activity启动流程(七)初始化目标Activity并执行相关生命周期流程


本篇博客编写思路总结和关键点说明:

为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便读者取舍和阅读!


引言

  如果我们将Activity的启动比喻成一场接力赛的话,那么通过我们前面博客Activity启动流程实现(五)请求并创建目标Activity进程以及更前面一系列博客的艰苦卓越的斗争,那么我们是已经完满的完成了前半场的接力赛了!我们发现上半场接力赛的最终落脚点在创建一个目标Activity所属的应用进程,并且Activity启动过程的上半场部分都还是在系统进程system_server和zygote携手并且共同完成的,其主要逻辑是对系统进程system_server内部的数据结构和状态进行相关的调整(主要是Task和Stack栈,目标Activity的ActivityRecord的创建,以及目标进程ProcessRecord的创建)。

但是上述Activity的启动接力赛还远远没有结束,我们知道当Zygote创建完一个应用进程之后,得到的仅仅是一个可以运行的载体,对于应用开发者来说这还远远不够,Android的四大组件还没有侵入到这个新创的进程之中。对于Android应用开发者来说,基本淡化了进程相关的概念,所以当zygote进程创建目标Activity进程之,还需要创建一个运行环境,就是Context,然后创建Application,然后再装载Provider等组件信息,经过如上步骤操作之后才是应用开发者所熟悉的Android应用。对于我们的目标Activity进程而言,也需要经历这个过程,本文分析的起点,正是基于前面博客Activity启动流程实现(五)请求并创建目标Activity进程的基础之上进行的。在本篇博客中我将会带领小伙们一起分析目标Activity所属进程启动之后的相关处理逻辑,其主要包括如下的相关子流程:

  • 注册目标Activity所属进程到system_server
  • 目标Activity所属进程创建Application实例对象

注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:

frameworks/base/services/core/java/com/android/server/am/--- ActivityManagerService.java--- ProcessRecord.java--- ActivityRecord.java--- ActivityResult.java--- ActivityStack.java--- ActivityStackSupervisor.java--- ActivityStarter.java--- TaskRecord.java frameworks/base/services/core/java/com/android/server/pm/--- PackageManagerService.javaframeworks/base/core/java/android/content/pm/
--- ActivityInfo.javaframeworks/base/core/java/android/app/--- IActivityManager.java--- ActivityManagerNative.java (内部包含AMP)--- ActivityManager.java--- AppGlobals.java--- Activity.java--- ActivityThread.java(内含AT)--- LoadedApk.java  --- AppGlobals.java--- Application.java--- Instrumentation.java--- IApplicationThread.java--- ApplicationThreadNative.java (内部包含ATP)--- ActivityThread.java (内含ApplicationThread)--- ContextImpl.java

并且在后续的源码分析过程中为了简述方便,会将做如下简述:

  • ApplicationThreadProxy简称为ATP
  • ActivityManagerProxy简称为AMP
  • ActivityManagerService简称为AMS
  • ActivityManagerNative简称AMN
  • ApplicationThreadNative简称ATN
  • PackageManagerService简称为PKMS
  • ApplicationThread简称为AT
  • ActivityStarter简称为AS,这里不要和ActivityServices搞混淆了
  • ActivityStackSupervisor简称为ASS

在正式开始今天博客相关源码分析前,还是先奉上今天博客将要涉及到的时序图以便小伙们先从整体上有个清晰的概括,然后再从细节开撸!

我们对上述的时序图简单的来注释一下:
绿色的组件表示的是运行在目标Activity所属进程中
蓝色的组件表示是运行在system_server进程中
Activity所属进程和system_server进程之间通过红色箭头表示的Binder完成了跨进程通信,配合完成了Activity启动的下半场接力赛
紫色箭头表示的是Handler内部信息传递


一.注册目标Activity进程到system_server

  也许部分小伙们看到这个标题,就会进行反驳了!啥,注册目标Activity进程到system_server?目标Activity进程不是由system_server中的AMS向zygote进程发起请求创建的吗,并且已经在AMS中建立了合适的ProcessRecord数据结构来保存目标Activity进程了,这里怎么又给我整个注册呢!这里我们先留一个悬念,卖个关子!我们先分析,后续再解答(也许不用我的解答,小伙们阅读了这个章节其中的深意就自行悟道了)!

还记得我们我们在前面博客中,分析到在目标Activity进程被创建之后会调用到ActivityThread中的main方法中,开启目标Activity进程的Java世界大门吗!而我们在这个章节会在上述的基础之上重点分析目标Activity进程是怎么注册(attach)到system_server进程中的。其核心的架构思想如下图所示:

1.1 ActivityThread.main(…)

  为了保持整个系列的连贯,我们从ActivityThread的main方法开始,它是一个静态方法,通过前面的博客知道它会在Actiivity进程创建的过程中通过反射调用到!

//[ActivityThread.java]
public static void main(String[] args) {...Environment.initForCurrentUser();...Process.setArgV0("<pre-initialized>");//创建主线程looperLooper.prepareMainLooper();ActivityThread thread = new ActivityThread();//attach到系统system_server进程thread.attach(false);//详见章节1.1if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}//主线程进入循环状态Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

该方法的职能是:

  • 调用attach,向system_server发起一个绑定操作,告诉AMS进程应启动完毕,可以进行其他事情了

  • 初始化应用进程的主线程的Looper,并开启loop消息循环

我们通常说,ActivityThread就是应用进程的主线程,这其实是一种笼统的说法,其实ActivityThread并非真正意义上的线程,它不是Thread的子类,只不过ActivityThread充当了主线程的职能,它初始化了一个消息队列。在ActivityThread对象构建时,会创建一个Handler对象,这个Handler对象所绑定的消息队列就是主线程的消息队列,后面主线程所作的任何事情都是通过往Handler中发送消息来完成的,所以说Android系统是基于消息驱动的。并且对于应用开发的小伙们来说都知道主线程默认实现了looper的,但是绝大部分却不一定知道是什么时候实现的,就是这个时候实现的。调用了Looper.loop()后,整个线程就陷入死循环,常规的调用流程已经结束了。

并且在此处存在一个经典的面试题:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
大伙可以先自行思考,思考。看看能否get到关键点。这里我也推荐一个答案Android中为什么主线程不会因为Looper.loop()里的死循环卡死?。

1.2 ActivityThread.attach(…)

  其实对于该方法,如果有阅读过Android系统启动之system_server进程创建大揭秘的小伙们应该不会陌生了,在system_server进程启动的流程中也会调用它只是传入的参数为true,而我们此时传入的参数为false而已!用以区别是system_server进程发起的绑定还是普通应用进程发起的绑定。

//[ActivityThread.java]final ApplicationThread mAppThread = new ApplicationThread();private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {...// 设置进程名。此时,还没有ApplicationInfo,所以用<pre-initialized>来命名应用进程android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());...//获取AMS服务端的远程代理对象AMPfinal IActivityManager mgr = ActivityManagerNative.getDefault();try {//通过Binder远程调用AMS的attachApplication方法mgr.attachApplication(mAppThread);//详见章节} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}...} else {//system_server进程会进入此分支}DropBox.setReporter(new DropBoxReporter());//为ViewRootImpl注册Config回调接口ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {...});}

上述方法执行的逻辑并不是很复杂,其主要就是两点:

  • 获取AMS服务的远程代理端AMP
  • 接着通过AMP借助Binder调用AMS的方法attachApplication,注意这里传递的mAppThread是一个匿名Binder实例,因此可以作为跨进程传递的参数。这里的mAppThread对象存在于应用进程,但会被传递到系统进程,在系统进程看来,此时的mAppThread就是操作应用进程的一个通信工具。后续,系统进程system_server如果想要向应用进程发起跨进程调用,也都需要通过mAppThread这个对象来完成相关的调度。

并且关于上述的执行逻辑我们在Android四大组件之Activity启动流程源码实现详解(一)中2.4章节已经有过详细的分析了,这里就不详细赘述了。并且如果小伙们对于什么是匿名Binder以及怎么传递的有不了解的小伙们,这篇博客Android Binder框架实现之何为匿名/实名Binder有详细分析就不过多歪歪了!

总之AMP.attachApplication的调用逻辑如下架构所示:

关于上述整个Binder IPC调用流程,可以使用如下伪代码来简述:

AMP.attachApplication(...)--->
BinderProxy.transact(...) --->
BpBinder.transact(...)--->
binder驱动传输--->
JavaBBinder.onTransact(...)--->
AMN.onTransact(..)--->
AMN.attachApplication(...) --->
AMS.attachApplication(...) --->

1.3 AMS.attachApplication(…)系统system_server进程处理attachApplication请求

  在这里AMS响应了目标Activity进程的attachApplication绑定请求,注意此时的attachApplication是执行在Binder线程中的。

//[ActivityManagerService.java]public final void attachApplication(IApplicationThread thread) {synchronized (this) {//获取调用进程端pidint callingPid = Binder.getCallingPid();final long origId = Binder.clearCallingIdentity();//attachApplicationLocked进行进一步处理attachApplicationLocked(thread, callingPid);Binder.restoreCallingIdentity(origId);}}

上述方法比较简单,接着调用attachApplicationLocked进行下一步的处理,注意此时多传入了一个参数pid。并且这里还有一点主要注意的是此时的参数类型IApplicationThread已经变成了匿名Binder的代理端了ATP了。IApplicationThread它串联起了AMS对App进程的生命周期及其其它的控制,其牵涉的类图关系表示如下

这里还有一个小窍门在Android源码中通常会见到带有Locked的方法,这类方法都比较特别,有点像大众屁股带字母的,需要特别处理。此类方法一般都需要进行持锁操作,而我们这里的attachApplicationLocked方法也不例外,可以看到它也被加锁操作了!

我们接着继续分析,attachApplicationLocked源码数量有点多啊(没有啥的前面那么难各种栈,启动模式都都分析过来了,这里就嗮嗮水的模式了)!

   private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {ProcessRecord app;long startTime = SystemClock.uptimeMillis();/*根据PID映射应用进程的ProcessRecord对象那么此处的ProcessRecord是什么时候创建并加入到mPidsSelfLocked中的呢,这个在该系列博客的五中有详细描述*/if (pid != MY_PID && pid >= 0) {synchronized (mPidsSelfLocked) {app = mPidsSelfLocked.get(pid);}} else {app = null;}if (app == null) {//当获取目标Activity进程的ProcessRecord失败,则会执行清理逻辑,并强制退出// 获取ProcessRecord对象失败,则做一些清理操作后退出if (pid > 0 && pid != MY_PID) {Process.killProcessQuiet(pid);} else {try {thread.scheduleExit();} catch (Exception e) {}}return false;}/*ProcessRecord对象之前绑定的进程还则,而当下需要将ProcessRecord绑定到一个新的进程所以需要将之前ProcessRecord所绑定的进程信息清除这个地方有点难理解,我们可以认为对此处的ProcessRecord进行复用,在复用之前需要做一些清理*/if (app.thread != null) {handleAppDiedLocked(app, true, true);}final String processName = app.processName;try {/*注册应用进程的DeathRecipient,当应用进程崩溃时,系统进程可以收到通知为啥要整这一套呢,主要是因为AMS服务监听到应用进程奔溃以后需要做一些资源包回收和数据结构的调整关于AppDeathRecipient可以参见大神gityuan的博客http://gityuan.com/2016/10/03/binder_linktodeath/*/AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {app.resetPackageList(mProcessStats);startProcessLocked(app, "link fail", processName);return false;}//将目标Activity进程的IApplicationThread匿名Binder代理端绑定到ProcessRecord对象app.makeActive(thread, mProcessStats);/****************************************************///这里为了演示方便,直接给出源码//[ProcessRecord.java]public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {if (thread == null) {...}thread = _thread;}        /****************************************************///继续进行其它的对ProcessRecord的赋值app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.forcingToForeground = null;updateProcessForegroundLocked(app, false, false);app.hasShownUi = false;app.debugging = false;app.cached = false;app.killedByAm = false;app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);//移除进程启动超时消息,就是前面ANR的埋雷机制mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);// 获取应用进程的所有ProviderList<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;//这又是一个ANR埋雷,后续在ContentProvider发布的时候会进行解除if (providers != null && checkAppInLaunchingProvidersLocked(app)) {Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);msg.obj = app;mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);}...try {...//省略debug和性能优化等相关配置的代码,这里就不赘述了//这里又是老熟人了,一路分析过来见过很多次了,此处发起跨进程调用,将一堆的信息传递给目标Activity应用进程thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,app.instrumentationUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked());//详见第二大章节updateLruProcessLocked(app, false, null);...} catch (Exception e) {//异常处理...}mPersistentStartingProcesses.remove(app);mProcessesOnHold.remove(app);//将该ProcessRecord从ProcessesOnHold列表中移除boolean badApp = false;boolean didSomething = false;//此处表示目标Activity进程是正常启动模式,此时肯定是if (normalMode) {try {//此处是关键啊,检查有没有待启动的activity,假如目标Activity是被冷启动则会在此处流程执行if (mStackSupervisor.attachApplicationLocked(app)) {//详见第三大章节didSomething = true;}} catch (Exception e) {badApp = true;}}if (!badApp) {try {//检查有没有待启动的service,这个我们再binderService有涉及到过didSomething |= mServices.attachApplicationLocked(app, processName);} catch (Exception e) {badApp = true;}}if (!badApp && isPendingBroadcastProcessLocked(pid)) {try {//查有没有待启动的receiverdidSomething |= sendPendingBroadcastsLocked(app);} catch (Exception e) {badApp = true;}}       .../*假如启动目标Activity,Service,或者BroadCast任何一个执行失败,都会kill掉目标Activity进程,并且执行handleAppDiedLocked的处理*/if (badApp) {app.kill("error during init", true);handleAppDiedLocked(app, false, true);return false;}if (!didSomething) {updateOomAdjLocked();}return true;}

上述源码的逻辑并不复杂,但是却很重要(感觉这是一句废话,如果不重要会有这个逻辑吗!),其主要逻辑如下:

  • 获取Activity目标进程在启动阶段由AMS向zygote进程发起请求时创建的ProcessRecord数据结构。其获取流程如下,首先通过Binder.getCallingPid()可以获取Binder接口的调用者所在进程的pid,接下来就能根据pid获取到应用进程对应的ProcessRecord数据记录(因为ProcessRecord在AMS中是以key-value键值对存储的,其key是pid)。如果ProcessRecord对象获取失败,则表示应用进程已经被杀掉,需要清除应用进程的数据;如果ProcessRecord之前所绑定的进程信息还在,则需要清除这些信息。

    还记得本章节开始的时候,我给小伙们留下的疑问吗!为啥目标Activity进程需要重新注册(attach)到system_server进程吗,上面的验证安全性是一个方面,另外一个方面就是通过注册(attach)的Binder远程调用传递匿名Binde类IApplicationThread给AMS,然后AMS就可以通过上述的匿名Binder继续对目标Activity进程的相关组件进行调度。

  • 为应用进程注册死亡通知AppDeathRecipient,它是存在于系统进程的对象,这样,当目标Activity进程被杀的时候,AMS会通过AppDeathRecipient来进行清理工作。并且关于Binder死亡通知有不熟悉的小伙们可以参见博客Binder死亡通知机制之linkToDeath。

  • 激活ProcessRecord对象。所谓“激活”,就是将ProcessRecord绑定到了一个具体的应用进程,绑定的标识就是将应用进程的ApplicationThread对象赋值给ProcessRecord.thread变量,注意此处的ApplicationThread是Binder的代理端,其实体端是在目标Activity进程端。

  • 获取目标Activity应用进程中所有注册的Provider(这个需要通过PackageManager来扫描进程所关联的包名,所有静态的Provider信息,即ProviderInfo对象,都会保存到ProcessRecord.pubProviders变量中,通常上述操作在应用安装和终端启动的时候会执行).

    在系统进程启动时,也曾经历过这个过程,系统进程对应的包名是”android”,扫描的是framework-res.apk的这个应用的信息。

  • 在进行一些调试与性能相关的变量设置之后,通过IApplicationThread.bindApplication()向目标Activity进程发起跨进程Binder调用,这样一来,诸如进程名、ApplicationInfo等等相关信息就传递给应用进程了。

  • 将信息传递给目标Activity应用程序以后,检查有没有四大组件等待着在该进程中运行,如果有,继续执行四大组件。不过这里只有Activity、Service和BroadCastReceiver三种组件,ContentProvider的发布会在bindApplication的时候进行。这里通过badApp/didSomething两个标识来记录调度的情况,其中badApp标识是否调度失败,默认为false,在依次调度Activity/Service/Broadcast的过程中,根据实际的情况,可能将其调整为true,表示调度失败了。一旦调度失败,则需要杀掉应用进程。而didSomething表示确有调度发生。在后文中,我们将着重分析Activity的调度,即ASS.attachApplicationLocked()函数。

    通常我们在冷启动Activity/Service的时候,就会进入上述的的启动点。而热启动的时候就不会走上述的启动点,而是直接将Actiivyt/Service拉起来就OK了!


二.创建Activity应用进程Application和并初始化应用进程运行环境

经过前面章节一顿猛虎一般的操作,system_server进程完成了对目标Actiivity应用进程的验证真身,接着应用进程会收到系统进程的反馈,然后开始应用进程的自我成长,此时目标Actiivity应用进程有了自己进程名,并且在接下来会构建出Android的运行环境,真正有了Android应用程序的概念,即Application,这时候应用进程才真正在Android的世界立足。此时此刻的目标Actiivity应用进程才是那个绝大部分应用开发者熟悉的它(即执行Applcation相关的生命周期)。

  待system_server进程中的AMS服务对目标Activity应用进程验证真身之后,会通过IApplicationThread跨进程回调到目标Activity应用进程,至于IApplicationThread是怎么实现跨进程调用的这里我也不赘述了(这个当然是Binder的功能,分析Android源码Binder是一道必须跨过去的坎啊)!总之通过thread.bindApplication实际上调用的就是ApplicationThread的schedulePauseActivity方法中去了,其调用过程可以使用下面的伪代码来表示:

ATP.bindApplication()--->
BinderProxy.transact() --->
BpBinder.transact()--->binder驱动传输--->JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.bindApplication(...) --->
ApplicationThread.bindApplication(...) --->

2.1 ApplicationThread.bindApplication(…)

  通过ATP的努力和我们的Binder框架的协助,我们跨越万水千山,完成了system_server所在进程到发起端所在Activity目的端应用进程调用过程,让我们接着分析看看目的端进程是怎么处理bindApplication的RPC请求的。我好难啊!在目的端应用进程中,ApplicationThread会在Binder线程中响应这个跨进程调用,进行一些简单的数据封装后,便向主线程抛出一个BIND_APPLICATION消息,这样一来,真正完成进程绑定的操作是在主线程的handleBindApplication()函数中。

//[ActivityThread.java]private class ApplicationThread extends ApplicationThreadNative {...public final void bindApplication(String processName, ApplicationInfo appInfo,List<ProviderInfo> providers, ComponentName instrumentationName,ProfilerInfo profilerInfo, Bundle instrumentationArgs,IInstrumentationWatcher instrumentationWatcher,IUiAutomationConnection instrumentationUiConnection, int debugMode,boolean enableBinderTracking, boolean trackAllocation,boolean isRestrictedBackupMode, boolean persistent, Configuration config,CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {//此处缓存的是通过bindApplication传递过来的PKMS,WMS,ALARM_SERVICE代理端if (services != null) {ServiceManager.initServiceCache(services);}setCoreSettings(coreSettings);//将AMS传递过来的参数封装到AppBindData 数据结构中AppBindData data = new AppBindData();data.processName = processName;...sendMessage(H.BIND_APPLICATION, data);}...}

巧用ActivityThread的主线程的Handler发送消息,我们接着分析

//[ActivityThread.java]
private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {...case BIND_APPLICATION:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");AppBindData data = (AppBindData)msg.obj;handleBindApplication(data);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;...}}
}

经过上述一番折腾从ApplicationThread的Binder线程中跳转到ActivityThread的主线程中handleBindApplication进行相关的处理,我们接着往下瞧一瞧,挖一挖!

//[ActivityThread.java]private void handleBindApplication(AppBindData data) {...//省略一些相关的参数设置//虽然说目标Actiivity进程在之间已经fork出来了,但是系统直到此时才是它的开端Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());// 虽然应用进程早就已经创建,但直到这时,才知道进程名是什么Process.setArgV0(data.processName);android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());... // 省略应用进程运行信息其它的一些设置代码,譬如语言,时区//创建LoadedApk对象,此处是关键,后续会专门分析data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//详见章节2.3//Android应用默认dpi相关设置if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)== 0) {mDensityCompatMode = true;Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);}updateDefaultDensity();//设置时间格式final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));DateFormat.set24HourTimePref(is24Hr);...//StrictMode相关的设置StrictMode.enableDeathOnNetwork();//应用进程相关的初始化代码,包含时区、StrictMode、调试模式等相关的设置,感兴趣的小伙们就自行研究吗...//创建ContextImpl对象;final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//详见章节2.4if (ii != null) {/*创建Instrumentation它也是我们的老熟人了,在前面分析的四大组件相关的生命周期调度中多次看到它了这里通过ClassLoader直接加载进行构建,在整个Android应用进程中它是唯一的网上很多博客将Instrumentation说为Android系统里面的一套控制方法或者”钩子“。这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行*/try {final ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();} catch (Exception e) {,,,}final ComponentName component = new ComponentName(ii.packageName, ii.name);//详见章节2.5mInstrumentation.init(this, instrContext, appContext, component,data.instrumentationWatcher, data.instrumentationUiAutomationConnection);...}} else {mInstrumentation = new Instrumentation();}//创建Application对象Application app = data.info.makeApplication(data.restrictedBackupMode, null);//详见章节2.6mInitialApplication = app;//安装providers,看来providers的安装都前于其它三大组件啊List<ProviderInfo> providers = data.providers;installContentProviders(app, providers);//这不Instrumentation刚被创建就被排上了用场执行Application.Create回调mInstrumentation.callApplicationOnCreate(app);//详见章节2.8}

此时我们已经将system_server进程传递过来的于应用进程相关的信息一股脑打包到了AppBindData数据结构中,接下来在该方法中会将上述的相关信息一已组装成Android应用程序的运行所必须的构成元素!其主要过程如下:

  • 首先是各种零碎资源的初始化,包括设定显示相关的configuration、初始化packageInfo、设置默认的Dpi、设置时间格式、设置strictMode,设置字体资源等等,为以后四大组件的运行提供运行环。

    何为strictMode模式,可以认为它是Android应用的一种运行模式,在Android 7版本之下会设定不能在主线程中访问网络,也不能在Uri中显式加入file等逻辑特点。

  • 创建对象LoadedApk

  • 创建Android运行环境ContextImpl

  • 创建Instrumentation对象

  • 创建Application对象。通过LoadedApk.makeApplication()函数,就能创建一个Application对象

  • 装载Providers。有了一个静态的ProviderInfo列表,但应用进程的ContentProvider还不能真正工作,因为ContentProvider对象还未创建。ActivityThread.installContentProviders()函数就是用来创建ContentProvider对象的。由此可见,在Application.onCreate()函数调用之前,进程的ContentProvider都已经创建完毕了,是不是有点惊讶ContentProvider的创建竟然早于Application.onCreate()方法

  • 调用Application.onCreate()方法。这个方法就是我们通常在Android应用开发者系统回调的方法,并且通常应用开发者会在此方法中做一些应用的全局设置

上述一顿咔咔的分析完了,并且主要逻辑也摆出来了,在接下来对其中各个逻辑依次分解之前,我们有一个重要的概念Context必须先拿出来,捯饬捯饬。这个对于应用开发者来说可能是最熟悉的陌生人了,经常会用到它,但是熟不熟悉就另外一说了。

2.2 最熟悉的陌生人Android Context

  Android Context是一个很神秘的存在,对于Android应用开发者来说它最最熟悉不过的了,几乎天天见(当然它不是大宝啊),在应用开发中大量的场景几乎都能看到它靓丽的身影(它是多么婀娜多姿,令人着迷啊!),跑题了啊,我们来简单归纳下Context的使用场景(最好的办法就是打开它,从深处剖析下):

  • 启动Activity(startActivityXXX(…)方法族)

  • 启动/停止Service(startServiceXXX(…)/stopServiceXXX(…)方法族)

  • 绑定/解绑Service(bindServiceXXX(…)/unbindService(…)方法族)

  • 发送广播(sendBroadcastXXX(…)/sendOrderedBroadcastXXX(…)方法族)

  • 注册/注销广播(registerReceiverXXX(…)/unregisterReceiver(…)方法族)

  • 获取ContentResolver(getContentResolver(…))

  • 获取类加载器 (getClassLoader(…))

  • 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等(openOrCreateDatabase(…)方法族)

  • 获取各种资源相关方法(getResources()/getString(…)/getColor(…)系列方法族)

  • 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等功能(getSharedPreferences(…)等系列方法族)

Context有点像你姥爷的百宝箱,应有尽有无所不有,你值得拥有!通过上述的梳理我们发现四大组件,各种资源操作以及其他很多场景都离不开Context, 那么Context到底是何方神圣呢? 中文意思绝大部分人都尊称其为“上下文”(有点承上启下的意味),而它顾名思义就是在某一个场景中本身所包含的一些潜在信息. 这就是上下文, 即某一个场景背后所隐藏的信息。是不是还是很抽象, 我们打个比方,举个栗子,假如你是王健林的私生子(肯定不是啦),那么你拥有的上下文是啥呢,肯定一个亿的小目标你是有了吗,给你五个亿创业,混不好就回家继承家业你是有了吗!此时的我手握Context,感觉世界都在脚下颤抖了,让我俯视一下天地众生吗。

2.2.1 Android Context类以及关联类简介

  Android 前面扯了这么多,我忍不住点开源码发现Android Context本身竟然是一个抽象类,这不是仙人跳吗!尼玛,别急吗Context虽然是一个抽象类,但是其徒子徒孙众多,我们常用的Activity/Service/Application/ContextImp和ContextWrapper都直接或间接继承自Context,其涉及的类图关系如下:

看了上面的类图,小伙们有何感想以及收获呢!这里我们直接给出关于Android Context系列的相关结论(这里只挑重点说):

  • 可以看到Context有两个直接继承子类ContextImpl和ContextWrapper,并且ContextWrapper又通过mBase(指向了ContextImpl),即ContextWrapper的核心工作都是交给ContextImpl)来完成,其二者之间是一个典型的代理模式

  • Application/Activity/Service通过attach()调用父类ContextWrapper的attachBaseContext(),从而设置父类成员变量mBase为ContextImpl对象,所以说真正执行Context使命的是ContextImpl,而ContextWrapper只是一个"傀儡"而已!

    一般情况下,使用代理而不直接使用某个对象,目的可能有两个:
    1.定制自己的行为
    2.不影响原对象
    其中Servcie和Application的父类ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为,因此:
    1.对于Service和Application而言,不直接继承ContextImp,是担心用户修改了ContextImp而导致错误的发生
    2.对于Activity而言,除了担心用户的修改之外,ContextImp和Activity本身对于Reource 以及Theme的相关行为是不同的

  • 我们知道Android同一应用中四大组件都属于同一个Application,那么这四大组件怎么获取对应的Application呢!
    1.Activity/Service:是通过调用其方法getApplication(),可主动获取当前所在mApplication;并且mApplication是由LoadedApk.makeApplication()过程所初始化的,这个后续会分析到
    2.Receiver:是通过其方法onReceive()的第一个参数指向通当前所在Application,也就是只有接收到广播的时候才能拿到当前的Application对象
    3.provider:目前没有提供直接获取当前所在Application的方法, 但可通过getContext()可以获取当前的ContextImpl

关于Android Context的介绍就先到这里了,其涉及的知识点还是蛮多的,感兴趣的可以看看直面底层:你对Context了解多少呢?和Android一个进程有多少个Context对象(答对的不多),这里我们就不过多细述了,我们分析完成了后面的章节,你也许就能解答上面的疑问了!

2.3 创建LoadedApk

  LoadedApk也算是我们的老朋友了,还记得在博客 Android四大组件之bindService源码实现详解中遇见过它,正是在它的帮助之下完成了bindService的流程,看来LoadedApk也是一个硬汉,Android四大组件离不开它啊!我们先看看LoadedApk的类图关系:

2.3.1 ActivityThread.getPackageInfoNoCheck(…)

相信小伙们在阅读源码的时候经常会遇到类似的XXX(,)和XXXNoCheck(…)的方法,通常这个是一对孪生兄弟,前者通常会做安全检查然后继续执行,而后者通常吗通常就没有这些套路了基本是直奔主题!

//[ActivityThread.java]public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,CompatibilityInfo compatInfo) {return getPackageInfo(ai, compatInfo, null, false, true, false);}

上述方法啥也没有干,直接调用了getPackageInfo方法继续处理,注意getPackageInfo被重载了不要搞错对象了!

//[ActivityThread.java]final ArrayMap<String, WeakReference<LoadedApk>> mPackages= new ArrayMap<String, WeakReference<LoadedApk>>();private LoadedApk getPackageInfo(ApplicationInfo aInfo, //这个是AMS通过bindApplication传递过来的Activity应用进程AndroidManifest中Application的的相关数据结构CompatibilityInfo compatInfo,//对ApplicationInfo数据结构的又一层封装ClassLoader baseLoader, //类加载器,此时为nullboolean securityViolation, //表示隐私,此时为falseboolean includeCode,//功能不详,此时为trueboolean registerPackage) //功能不详,此时为false{//多用户情况的判断final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));synchronized (mResourcesManager) {WeakReference<LoadedApk> ref;if (differentUser) {ref = null;} else if (includeCode) {//进入此分支,从mPackages查询,由于前面没有创建并且存储过,所以此处获取的为nullref = mPackages.get(aInfo.packageName);} else {ref = mResourcePackages.get(aInfo.packageName);}LoadedApk packageInfo = ref != null ? ref.get() : null;if (packageInfo == null || (packageInfo.mResources != null&& !packageInfo.mResources.getAssets().isUpToDate())) {//直接new出一个packageInfo =new LoadedApk(this, aInfo, compatInfo, baseLoader,securityViolation, includeCode &&(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);//详见2.3.2//system_server进程会进入此分支,这里不会进入此分支if (mSystemThread && "android".equals(aInfo.packageName)) {packageInfo.installSystemApplicationInfo(aInfo,getSystemContext().mPackageInfo.getClassLoader());}if (differentUser) {} else if (includeCode) {//将前面创建的LoadedApk放入mPackages列表mPackages.put(aInfo.packageName,new WeakReference<LoadedApk>(packageInfo));} else {mResourcePackages.put(aInfo.packageName,new WeakReference<LoadedApk>(packageInfo));}}return packageInfo;}}

上述方法比较简单l

  • 判断mPackages中能否通过包名找到合适的LoadedApk实例对象,如果没有则直接创建,然后将创建的LoadedApk对象放入到mPackages中.

    mPackages的数据类型为ArrayMap<String, WeakReference>,记录着每一个包名所对应的LoadedApk对象的弱引用,所以对于应用进程来说LoadedApk通常是唯一的!

好了至此我们的LoadedApk的创建流程到此over了,革命还没有成功,继续接着往下干!

2.3.2 LoadedApk的构建方法

这里我们还是简单的看下此处的LoadedApk构造方法,可以看到在构造方法中记录了Activity运行所在的ActivityThread、Activity所在的应用程序信息、Activity所在应用进程的包名、Activity所在应用进程的库路径、Activity所在应用进程的数据存储路径、类加载器和应用程序所使用的资源等信息。

//[LoadedApk.java]public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,CompatibilityInfo compatInfo, ClassLoader baseLoader,boolean securityViolation, boolean includeCode, boolean registerPackage) {mActivityThread = activityThread;setApplicationInfo(aInfo);/*****************************************************************************///这里为了演示方便,直接将源码搬出来!private void setApplicationInfo(ApplicationInfo aInfo) {final int myUid = Process.myUid();aInfo = adjustNativeLibraryPaths(aInfo);mApplicationInfo = aInfo;mAppDir = aInfo.sourceDir;mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;mSplitAppDirs = aInfo.splitSourceDirs;mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;mOverlayDirs = aInfo.resourceDirs;mSharedLibraries = aInfo.sharedLibraryFiles;mDataDir = aInfo.dataDir;mLibDir = aInfo.nativeLibraryDir;mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);mDeviceProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceProtectedDataDir);mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir);}        /*****************************************************************************/mPackageName = aInfo.packageName;mBaseClassLoader = baseLoader;mSecurityViolation = securityViolation;mIncludeCode = includeCode;mRegisterPackage = registerPackage;mDisplayAdjustments.setCompatibilityInfo(compatInfo);}

2.4 创建ContextImpl

  在前面章节我们大谈彻谈了一番Context类以及关联类,这不说曹操曹操就到了!前面我们知道ContextImpl是Context系列类中的实权派,最终相关Context上下文的操作都是由ContextImpl来执行的。我们就来会会它!

2.4.1 ContextImpl.createAppContext(…)

//[ContextImpl.java]static ContextImpl createAppContext(ActivityThread mainThread, //这里传入的是ActivityThread的引用LoadedApk packageInfo) //前面构建的LoadedApk实例对象引用{if (packageInfo == null) throw new IllegalArgumentException("packageInfo");return new ContextImpl(null, mainThread,packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);}

createAppContext的逻辑比较简单:

  • 该方法首先对传入的参数进行合法性的判断,如果不合法,则直接抛出异常,game over
  • !接着直接通过传入的参数,通过私有构造方法构造一个ContextImpl实例返回.

我们接着继续分析!

2.4.2 ContextImpl初始化

//[ContextImpl.java]
class ContextImpl extends Context {final ActivityThread mMainThread;final LoadedApk mPackageInfo;private final IBinder mActivityToken;private final String mBasePackageName;private Context mOuterContext;//缓存Binder服务final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) {mOuterContext = this; //ContextImpl对象mMainThread = mainThread; // ActivityThread赋值mPackageInfo = packageInfo; // LoadedApk赋值mActivityToken = activityToken;//这个通常是Activity在构建其ContextImpl时候传入的mBasePackageName = packageInfo.mPackageName; //mBasePackageName通常等于应用进程包名...}
}

木有啥难点,主要是将传递过来的参数对ContextImpl实例对象进行初始化!

并且这里需要注意地是,创建ContextImpl的方式有多种, 不同的组件初始化调用不同的方法,如下:-
Activity: 调用createBaseContextForActivity初始化;
Service/Application: 调用createAppContext初始化;
Provider: 调用createPackageContext初始化;
BroadcastReceiver: 直接从Application.getBaseContext()来获取ContextImpl对象;

2.5 构建Instrumentation并初始化它

  Android四大组件生命周期的调度都离不开Instrumentation它,那么我们就来揭开其庐山真面目看看它是怎么被构建和初始化的!在章节2.1我们看到它是通过类加载器进行直接加载创建的(这个怎么加载的我们就忽略了),我们只看其初始化和构造。

2.5.1 Instrumentation.init(…)

//[Instrumentation.java]//构造方法啥也没有干,看来是通过其它public Instrumentation() {}/*package*/ final void init(ActivityThread thread,//持有对ActivityThread实例对象的引用Context instrContext, Context appContext, ComponentName component, IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {mThread = thread;mMessageQueue = mThread.getLooper().myQueue();mInstrContext = instrContext;mAppContext = appContext;mComponent = component;mWatcher = watcher;mUiAutomationConnection = uiAutomationConnection;}

创建Instrumentation就到这了!没有啥过多强调的了!好吗,其实还是有点想重点强调的就是一个应用进程中,Instrumentation实例对象都是唯一的,这不是我的遗言啊!

2.6 构建Application

  分析到这里,估计很多小伙们都要骂娘了!博主这不是操蛋吗,给我扯了这么久,大标题说好的是啥构建Application,到现在连个鬼影子都没有看到!各位小伙们,这不就给安排上了吗!

有了前面2.3章节LoadedApk对象,接下来可以创建Application对象, 该对象每一个Apk应用进程只会创建一次(地球人都知道不是!)。

2.6.1 LoadedApk.makeApplication(…)

//[LoadedApk.java]public Application makeApplication(boolean forceDefaultAppClass,//该值是从AMS中传递过来的Instrumentation instrumentation) //此时的instrumentation已经被指定为null{/*保证一个LoadedApk对象只创建一个对应的Application对象实例,和我们章节开始所说的每个应用进程只拥有一个Application相对应*/if (mApplication != null) {return mApplication;}...Application app = null;/*获取Application名称,即我们在AndroidManifest中Application的名称如果我们没有实现自己的Application,通常appClass就是null了*/String appClass = mApplicationInfo.className;//强制给Application一个名称,你懂的if (forceDefaultAppClass || (appClass == null)) {appClass = "android.app.Application";}try {java.lang.ClassLoader cl = getClassLoader();//system_server进程除外if (!mPackageName.equals("android")) {//设置当前线程的Context ClassLoaderinitializeJavaContextClassLoader();}//这个在章节2.4中已经有详细分析过了,并且你会发现和2.4的参数取值都是一样的ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//创建Application对象, 并将appContextH和县创建的Application关联起来app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);//详见章节2.6.2appContext.setOuterContext(app);} catch (Exception e) {,,,}    //这个地方不是很明白,ActivityThread通常不是一个应用进程只有一个,而且Application也是唯一的吗,为啥这里要整个//存放Application的列表进行存放呢,纳尼mActivityThread.mAllApplications.add(app);mApplication = app;//将刚创建的app赋值给mApplicationif (instrumentation != null) {//此时的instrumentation已经被强制位null,所以此分支肯定不会走...}...return app;}

来来来,我们一起看看makeApplication方法是怎么给我们创建出一个Application对象实例的,其主要的的执行逻辑如下:

  • 首先做一些策略检查,即保证一个LoadedApk对象只能创建一个对应的Application对象实例,和我们章节开始所说的每个应用进程只拥有一个Application相对应
  • 接着获取当前应用的ClassLoader对象,如果不是system_server(system_server进程的包名就是"android")进程则调用initializeJavaContextClassLoader的过程(和设置类加载器有关)
  • 接着和2.4章节类似,如法炮制根据当前ActivityThread对象来创建相应的ContextImpl对象
  • 接着调用前面创建的Instrumentation实例对象的方法newApplication,传入相关的参数创建Application对象, 并初始化其成员变量,其中它的成员变量mBase指向新创建ContextImpl,另一个成员变量mLoadedApk指向当前所在的LoadedApk对象。这里小伙们发现了没有他们之间的相互引用真多啊!
  • 将新创建的Application对象保存到ContextImpl的成员变量mOuterContext

    不知道小伙们对于此处是否有疑问(如果没有,恭喜你你已经很牛逼了)! 这里的appContext 是是一个方法内部变量,然后将新创建的Application对象保存到ContextImpl的成员变量mOuterContext有啥意义呢!方法运行一结束不就被销毁了吗,其实不然因为Application对象创建的时候持有对它的引用,而且我们可以通过getApplication获取对唯一的Applcation实例对象,然后通过Application就可以获取到这里的ContextImpl实例对象了。

并且这里还存在一个知识点就是,关于普通的应用进程来说,应用Application类名采用的是App中声明的应用类名,即AndroidManifest中定义的类名,有两种特殊情况会强制 设置应用类名为”android.app.Application”:

  • 当forceDefaultAppClass =true, 目前只有system_server进程初始化包名为”android”的过程才会调用;
  • App没有自定义应用Application类名的情况

2.6.2 Instrumentation.newApplication(…)

//[Instrumentation.java]public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {return newApplication(cl.loadClass(className), context);}static public Application newApplication(Class<?> clazz, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {Application app = (Application)clazz.newInstance();app.attach(context);//执行attach操作return app;}

这里的newApplication没有过多的可说的,我们接着看Applcation被创建以后的attach操作!

final void attach(Context context) {attachBaseContext(context); //Application的mBasemLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

一路过来小伙们有没有发现在Android源码中好多attach方法啊,这里的attach方法主要功能如下:

  • 将新创建的ContextImpl对象保存到Application的父类成员变量mBase;
  • 将当前所在的LoadedApk对象保存到Application的父员变量mLoadedApk;

2.7 安装providers

对于Provider,应用开发的小伙们应该是比较熟悉的了。Provider通常被翻译为内容提供者,通常是一个App对第三方提供的一些数据接口,譬如在我们的Android中原生应用就可以看到许多的Provider,如下:

XXX:/system/priv-app # ls | grep Provider
ContactsProvider
DownloadProvider
ExternalStorageProvider
MediaProvider
MtpDocumentsProvider
SettingsProvider
TelephonyProvider

那我们简单看下Provider是怎么通告出去的。

2.7.1 ActivityThread.installContentProviders(…)

//[ActivityThread.java]private void installContentProviders(Context context, List<ProviderInfo> providers) //注意这里的参数,是AMS传递过来的在AndroidManifest中的注册的Provider信息{final ArrayList<IActivityManager.ContentProviderHolder> results =new ArrayList<IActivityManager.ContentProviderHolder>();for (ProviderInfo cpi : providers) {if (DEBUG_PROVIDER) {StringBuilder buf = new StringBuilder(128);buf.append("Pub ");buf.append(cpi.authority);buf.append(": ");buf.append(cpi.name);Log.i(TAG, buf.toString());}IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);if (cph != null) {cph.noReleaseNeeded = true;results.add(cph);}}try {//这个就不分析了,总之会注册到AMS中去ActivityManagerNative.getDefault().publishContentProviders(getApplicationThread(), results);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

publishContentProviders这个不是本篇的重点,后续有时间会抽一个专门的时间来分析其逻辑它是怎么注册到AMS中去的。我们这里重点关注下installProvider。

2.7…2 ActivityThread.installContentProviders(…)


private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {Context c = null;ApplicationInfo ai = info.applicationInfo;if (context.getPackageName().equals(ai.packageName)) {c = context;} else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {c = mInitialApplication;} else {//step 1 && 2: 创建LoadedApk和ContextImpl对象c = context.createPackageContext(ai.packageName,Context.CONTEXT_INCLUDE_CODE);}final java.lang.ClassLoader cl = c.getClassLoader();//step 3: 创建ContentProvider对象localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();provider = localProvider.getIContentProvider();//step 4: ContextImpl都attach到ContentProvider对象//step 5: 并执行回调onCreatelocalProvider.attachInfo(c, info);} else {...}...return retHolder;
}

小伙们有没有发现,这个方法执行的流程和章节2.1惊人的相似啊,特别是其过程主要功能和作用:

  • 创建对象LoadedApk(此处并不会真的去创建了,前面以前有创建过了)
  • 创建对象ContextImpl;
  • 创建对象ContentProvider;
  • ContextImpl都attach到ContentProvider对象;
  • 执行ContentProvider的onCreate回调;

2.8 执行目标应用进程Application的onCreate方法

历经千辛万苦,我们的Application也被创建出来了,而我们知道在应用开发中Application中的onCreate通常会被系统回调执行,我们看看它是怎么执行的。

//[Instrumentation.java]public void callApplicationOnCreate(Application app) {app.onCreate();}

好吗,我还能说啥没有啥好说了,简单明了一下子就完了。

2.9 应用进程创建Application和并初始化应用进程运行环境小结

  至此,Activity应用进程Application和并初始化应用进程运行环境就已经分析完成了,此时我们的应用进程已经具备了Android应用进程的基本条件,四大组件的相关运行环境也已经OK了,此时只待我们的四大组件在上面遨游,自由发挥了。这里我们还是对整个过程简单总结一下,看看我们都取得了那些阶段性的成果:

  • 首先是各种零碎资源的初始化,包括设定显示相关的configuration、初始化packageInfo、设置默认的Dpi、设置时间格式、设置strictMode,设置字体资源等等,为以后四大组件的运行提供运行环。

    何为strictMode模式,可以认为它是Android应用的一种运行模式,在Android 7版本之下会设定不能在主线程中访问网络,也不能在Uri中显式加入file等逻辑特点。

  • 创建对象LoadedApk

  • 创建Android运行环境ContextImpl

  • 创建Instrumentation对象

  • 创建Application对象。通过LoadedApk.makeApplication()函数,就能创建一个Application对象

  • 装载Providers。有了一个静态的ProviderInfo列表,但应用进程的ContentProvider还不能真正工作,因为ContentProvider对象还未创建。ActivityThread.installContentProviders()函数就是用来创建ContentProvider对象的。由此可见,在Application.onCreate()函数调用之前,进程的ContentProvider都已经创建完毕了,是不是有点惊讶ContentProvider的创建竟然早于Application.onCreate()方法

  • 调用Application.onCreate()方法。这个方法就是我们通常在Android应用开发者系统回调的方法,并且通常应用开发者会在此方法中做一些应用的全局设置

上述整个流程,可以通过如下伪代码来表述:

ATP.bindApplication()--->
BinderProxy.transact() --->
BpBinder.transact()--->binder驱动传输--->JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.bindApplication(...) --->
ApplicationThread.bindApplication(...) --->
ActivityThread.H.sendMessage(H.BIND_APPLICATION,...) --->
ActivityThread.handleBindApplication(...) --->data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//创建LoadedApkfinal ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//构建ContextImplfinal ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();//构建InstrumentationinstallContentProviders(app, data.providers);//安装ContentProvidermInstrumentation.callApplicationOnCreate(app);//调用Application的onCreate方法

总结

  至此注册Activity应用进程到system_server以及创建Activity应用进程Application到此处就完结了,此时此刻的你一路走来的心路历程是啥呢!是喜是悲,还是悲喜交加呢!

不知道小伙发现没有一路走来在我们冷启动Actiivty的时候,在其启动过程中牵涉到了非常多的进程交互,为什么Android会这样设计呢,这又是为什么呢!在回答这个问题之间我们先来看看,这其中涉及的交互逻辑图。

就我结合众家所言,Android的妈咪谷歌这么设计的主要原因如下:

首先Android应用试运行在Linxu中进程概念的基础之上的,所以Android应用程序需要一个可以运行的进程,这个进程的创建需要通过某种手段通知系统进程system_server,譬如启动Activity,从而引发Zygote孵化出一个应用进程(此时的进程还只是一个普通的进程,还不具备Android应用的相关特性);

刚出生的应用进程来到Android的世界,还什么都不懂,什么都没有,甚至连个正经儿的名字都没有,这时,应用进程极需要将自己加入到Android的社会关系中。应用进程知道,在Android世界中,有一个中心进程,即系统进程system_server,运行在系统进程中有一个管理者,即AMS。所以,应用进程就向AMS发起了“绑定”请求;

AMS在收到“绑定”请求后,迅速了解到情况,知道应用进程因何而来,为何而去,把应用进程需要生存下去的信息传递给它,譬如ApplicationInfo,PrivderInfo等,让应用进程继续来完善自我,茁壮成长;

应用进程在收到系统进程的反馈之后,开始自我成长,有了进程名,构建出Android的运行环境,真正有了Android应用程序的概念,即Application,这时候应用进程才能真正在Android的世界立足,成为众多应用开发者所熟知的Android App应用

总之Acitivity冷启动过程中,多次在system_server进程和Zygote进程以及Activity目标进程中这么来回折腾主要是为了为Android的应用创建良好的运行环境和淡化相关进程的概念,让上层应用开发者只需要关注四大组件的相关知识点,而不必关注底层是怎么运行的,从而达到Android应用的开发难度,吸引更多的开发者完善Android的应用生态系统!不可谓不妙哉啊!


写在最后

  Activity启动流程(六)注册目标Activity进程到system_server进程以及创建目标Activity进程Application这里就要告一段落了,从前面的分析可以看出来,此时我们已经将目标Activity将要运行的应用环境构建OK了,万事俱备只欠东风了!只待我们将目标Activity启动,然后执行其正常显示的生命周期流程即可,而这也是我们接下来的博客需要继续分析的了Activity启动流程(七)初始化目标Activity并执行相关生命周期流程。好了,青山不改绿水长流先到这里了。如果本博客对你有所帮助,麻烦关注或者点个赞,如果觉得很烂也可以踩一脚!谢谢各位了!!

Activity启动流程(六)注册目标Activity进程到system_server进程以及创建目标Activity进程Application相关推荐

  1. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 浅谈Activity启动流程

    概述 Activity的启动方式有两种,一种是显式的,一种是隐式的. 而且,启动的 Activity 和原 Activity 的进程关系的不同又可以分为两种情况,一种是在同一个进程,另外一种情况是开启 ...

  4. Android13 Activity启动流程

    前言 最近因为工作的原因,需要了解Activity的启动流程,网上找了很多,但是没有找到一遍关与Android13的,于是我决定自己写一篇.在学习的过程中,发现这个流程实在太复杂,如果面面俱到估计所需 ...

  5. framework之Activity启动流程(基于Android11源码)

    一步步看,你就会对activity的启动流程有深刻的认知. 引言 Android11上,Activity的启动流程与Android10的实现(可以参考Activity的启动过程详解(基于10.0源码) ...

  6. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  7. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  8. 深入分析Android 9.0源代码——Activity启动流程

    引言 点击此处查看<深入分析Android 9.0源代码>系列的组织结构和相关说明. 1 应用进程发起启动请求 本章的调用流程如下图所示: (Context) Activity Instr ...

  9. Activity启动流程-AMS

    Activity的启动的基本介绍 因为关于AMS内部类的关系,例如stack.task,已有了相关的文档做介绍,本文不再赘述细说,基于相关文档以及代码的学习,本文主要集中篇幅集中于第三章,描述在Act ...

最新文章

  1. Bitcoin ABC首席开发者回应有关比特币现金的提问(二)
  2. libxml解析的attributes参数理解
  3. pycharm 中HTML代码的对齐
  4. 解决 Idea 卡在 Resolving Maven dependencies ...
  5. postgreSQL源码分析——索引的建立与使用——GIST索引(1)
  6. Android变量longpress,Android:在自定义WebView中从onLongPress打开ContextMenu
  7. php,mysql 小测验 习题解析
  8. 淘宝MySQL十大经典案例pdf
  9. 程序员必须掌握的 CPU 硬核干货!
  10. 数据结构笔记(十四)-- 串的模式匹配算法
  11. LINUX进程内存占用查看
  12. 英语语料库与英文写作
  13. canvas文字粒子动画js特效
  14. 《西部世界》与《头号玩家》:哪个才是人类与人工智能相处的正确方式?
  15. android开发网站的流程图,Android_客户端开发流程图及案例.pdf
  16. #大三狗的日常总结与反思03#
  17. 【笔记】因子投资:方法与实践
  18. C/C++ 如何调用Lua脚本,Windows以及Linux版本演示
  19. vscode保存卡顿,显示正在从“‘Vetur‘, ‘ESLint‘”获取代码操作([配置]
  20. 简述余弦函数cos(x)和反余弦函数acos(x)------(附Demo案例)

热门文章

  1. [c语言]——跳水比赛
  2. ios的常用的一些方法
  3. 样条线怎么挤出平面_浅谈“沿样条线挤出”在多边形建模中的应用
  4. 拼题python答案_Pta mooc“Python编程浙江大学”拼图第6章问答,PTAMOOCPython,程序设计,拼题,题目,集,第六章,及,代码,答案...
  5. 关于程序员的那些表情包~
  6. 主题元素html,海洋元素主题的网页设计
  7. 新一代跟踪抠图软件—Mokey问世(图)_软件_科技时代_新浪网
  8. tplink886n变无线打印服务器,TP-LINK TL-WR886N如何设置无线桥接
  9. 一文带你干懂 sRGB linear-RGB natural-RGB XYZ xyY 以及他们之间的转换
  10. 127.0.0.1是回送地址,指本地机