Instant Run 和安卓 Application 启动流程有何关系
关于 Application 启动流程网上有很多文章分析, 但绝大多数文章都是为了讲 Application 启动流程而讲的, 因此绝大多数文章就是分析源码流程, 但是大家都懂的, Application 启动流程的这个源码是相当的的复杂, 绝大多数人看了不一定能够很好的理解, 因为源码细节太繁琐, 只见树木不见森林, 没有一个很好的目标感, 而我觉得分析源码最好的方式就是带着某个问题去看, 即我要分析这个源码是为了解决什么疑惑? 这样才能更好的做到点到为止, 能放能收, 不过分注重细节, 也不漏掉某些重要细节 (比如关于 Instant Run 中类加载器部分的逻辑网上绝大部分文章就是一句简单的双亲委派带过, 但是实际上 Instan Run 关于类加载器部分的精妙之处却是其设计思想, 不清楚的同学可以看从 ClassNotFoundException 看 java 的类加载机制和 Instant Run 类加载部分的设计 http://huqi.tech/index.php/2018/02/23/classnotfoundexception_instant_run/) 但很遗憾网上的绝大多数文章就是把源码流程分析一遍. 最近在看 Instant Run 相关的代码, 发现 Instant Run 中的替换 Application 这个过程的细节代码需要了解 Application 的启动流程, 因此觉得这是一个很好的去了解 Applicaiton 启动流程的契机, 即了解 Application 的启动流程来解决看 Instant Run 中的替换 Application 这个过程的细节代码的疑惑.
为何需要替换 Applicaiton
那么为何需要替换 Application 呢? 或者说那些业务场景下需要替换 Application 呢? 有些时候我们为了是实现一些特殊的需求, 比如在自定义的 ClassLoader 中加载一个 dex 的所有代码 (当然包括 Application 类), 再比如一般的 apk 加固是会用自己的 Applicaiotn 去接管用户的 apk 的启动, 再比如 Instant Run, 这些场景都有一个共同的特点就是希望自己的某些代码在用户的代码运行之前执行, 而我们知道对于应用层而言, 用户的 Application(假设叫做 OriginalApplication) 是最早执行的代码, 那这就产生了一个悖论, OriginalApplication 是最早执行的, 在其执行前没有任何入口可以提供给我们去执行我们的代码, 因此只能用我们的 Application(假设叫做 ProxyApplication)替换掉用户的 Application, 然后在 ProxyApplication 中去接管执行 OriginalApplication 的业务逻辑.
如何做到替换 Application, 从而保证替换之后整个 app 使用的 Application 还是原来用户自定义的 Application
前面说过 Instan Run 中存在替换用户自定义 Application 的过程, 我们看下 Instant Run 是如何实现的, 主要逻辑在 MonkeyPatcher.java 这个类的 monkeyPatchApplication 函数中. 如何下是替换核心逻辑.
- public static void monkeyPatchApplication( Context context,
- Application bootstrap,
- Application realApplication,
- String externalResourceFile) {
- /*
- The code seems to perform this:
- Application realApplication = the newly instantiated (in attachBaseContext) user app
- currentActivityThread = ActivityThread.currentActivityThread;
- Application initialApplication = currentActivityThread.mInitialApplication;
- if (initialApplication == BootstrapApplication.this) {
- currentActivityThread.mInitialApplication = realApplication;
- // Replace all instance of the stub application in ActivityThread#mAllApplications with the
- // real one
- List<Application> allApplications = currentActivityThread.mAllApplications;
- for (int i = 0; i <allApplications.size(); i++) {
- if (allApplications.get(i) == BootstrapApplication.this) {
- allApplications.set(i, realApplication);
- }
- }
- // Enumerate all LoadedApk (or PackageInfo) fields in ActivityThread#mPackages and
- // ActivityThread#mResourcePackages and do two things:
- // - Replace the Application instance in its mApplication field with the real one
- // - Replace mResDir to point to the external resource file instead of the .apk. This is
- // used as the asset path for new Resources objects.
- // - Set Application#mLoadedApk to the found LoadedApk instance
- ArrayMap<String, WeakReference<LoadedApk>> map1 = currentActivityThread.mPackages;
- for (Map.Entry<String, WeakReference<?>> entry : map1.entrySet()) {
- Object loadedApk = entry.getValue().get();
- if (loadedApk == null) {
- continue;
- }
- if (loadedApk.mApplication == BootstrapApplication.this) {
- loadedApk.mApplication = realApplication;
- if (externalResourceFile != null) {
- loadedApk.mResDir = externalResourceFile;
- }
- realApplication.mLoadedApk = loadedApk;
- }
- }
- // Exactly the same as above, except done for mResourcePackages instead of mPackages
- ArrayMap<String, WeakReference<LoadedApk>> map2 = currentActivityThread.mResourcePackages;
- for (Map.Entry<String, WeakReference<?>> entry : map2.entrySet()) {
- Object loadedApk = entry.getValue().get();
- if (loadedApk == null) {
- continue;
- }
- if (loadedApk.mApplication == BootstrapApplication.this) {
- loadedApk.mApplication = realApplication;
- if (externalResourceFile != null) {
- loadedApk.mResDir = externalResourceFile;
- }
- realApplication.mLoadedApk = loadedApk;
- }
- }
- */
- // BootstrapApplication is created by reflection in Application#handleBindApplication() ->
- // LoadedApk#makeApplication(), and its return value is used to set the Application field in all
- // sorts of Android internals.
- //
- // Fortunately, Application#onCreate() is called quite soon after, so what we do is monkey
- // patch in the real Application instance in BootstrapApplication#onCreate().
- //
- // A few places directly use the created Application instance (as opposed to the fields it is
- // eventually stored in). Fortunately, it's easy to forward those to the actual real
- // Application class.
- try {
- // Find the ActivityThread instance for the current thread
- Class<?> activityThread = Class.forName("android.app.ActivityThread");
- Object currentActivityThread = getActivityThread(context, activityThread);
- // Find the mInitialApplication field of the ActivityThread to the real application
- Field mInitialApplication = activityThread.getDeclaredField("mInitialApplication");
- mInitialApplication.setAccessible(true);
- Application initialApplication = (Application) mInitialApplication.get(currentActivityThread);
- if (realApplication != null && initialApplication == bootstrap) {
- mInitialApplication.set(currentActivityThread, realApplication);
- }
- // Replace all instance of the stub application in ActivityThread#mAllApplications with the
- // real one
- if (realApplication != null) {
- Field mAllApplications = activityThread.getDeclaredField("mAllApplications");
- mAllApplications.setAccessible(true);
- List<Application> allApplications = (List<Application>) mAllApplications
- .get(currentActivityThread);
- for (int i = 0; i <allApplications.size(); i++) {
- if (allApplications.get(i) == bootstrap) {
- allApplications.set(i, realApplication);
- }
- }
- }
- // Figure out how loaded APKs are stored.
- // API version 8 has PackageInfo, 10 has LoadedApk. 9, I don't know.
- Class<?> loadedApkClass;
- try {
- loadedApkClass = Class.forName("android.app.LoadedApk");
- } catch (ClassNotFoundException e) {
- loadedApkClass = Class.forName("android.app.ActivityThread$PackageInfo");
- }
- Field mApplication = loadedApkClass.getDeclaredField("mApplication");
- mApplication.setAccessible(true);
- Field mResDir = loadedApkClass.getDeclaredField("mResDir");
- mResDir.setAccessible(true);
- // 10 doesn't have this field, 14 does. Fortunately, there are not many Honeycomb devices
- // floating around.
- Field mLoadedApk = null;
- try {
- mLoadedApk = Application.class.getDeclaredField("mLoadedApk");
- } catch (NoSuchFieldException e) {
- // According to testing, it's okay to ignore this.
- }
- // Enumerate all LoadedApk (or PackageInfo) fields in ActivityThread#mPackages and
- // ActivityThread#mResourcePackages and do two things:
- // - Replace the Application instance in its mApplication field with the real one
- // - Replace mResDir to point to the external resource file instead of the .apk. This is
- // used as the asset path for new Resources objects.
- // - Set Application#mLoadedApk to the found LoadedApk instance
- for (String fieldName : new String[]{"mPackages", "mResourcePackages"}) {
- Field field = activityThread.getDeclaredField(fieldName);
- field.setAccessible(true);
- Object value = field.get(currentActivityThread);
- for (Map.Entry<String, WeakReference<?>> entry :
- ((Map<String, WeakReference<?>>) value).entrySet()) {
- Object loadedApk = entry.getValue().get();
- if (loadedApk == null) {
- continue;
- }
- if (mApplication.get(loadedApk) == bootstrap) {
- if (realApplication != null) {
- mApplication.set(loadedApk, realApplication);
- }
- if (externalResourceFile != null) {
- mResDir.set(loadedApk, externalResourceFile);
- }
- if (realApplication != null && mLoadedApk != null) {
- mLoadedApk.set(realApplication, loadedApk);
- }
- }
- }
- }
- } catch (Throwable e) {
- throw new IllegalStateException(e);
- }
- }
可以看到这个函数绝大部分都是注释, 而且从注释中可以看到替换 Application 主要包括 3 部分:
将 ActivityThread 类的 mInitialApplication 成员替换为真实的 Application, 所谓真实 Application 即用户原本定义的 Application
将 ActivityThread 类的 mAllApplications 域 (为 ArrayList<Application > 对象) 所有的值替换为真实的 Application(Replace all instance of the stub application in ActivityThread#mAllApplications with the real one)
枚举所有的 LoadedApk 或者 PackageInfo 对象(这 2 个对象的作用基本一致, 只不过在不同版本的 sdk 中因为历史原因起名不同而已), 将这些对象中的 mApplication 域设置为真实的 Application. 同样也要把 Application 的 mLoadedApk 设置为该 LoadedApk 对象.(Enumerate all LoadedApk (or PackageInfo) fields in ActivityThread#mPackages and ActivityThread#mResourcePackages and do two things:1 Replace the Application instance in its mApplication field with the real one.2 Set Application#mLoadedApk to the found LoadedApk instance)
可以看到替换 Application 对象涉及到 2 个重要类, ActivityThread 和 LoadedApk(或者 PackageInfo), 替换 Application 就是把 ActivityThread 对象中已经赋过值的 mInitialApplication,mAllApplications 和 LoadedApk 对象中的 mApplication 替换为用户原本的 Application. 那么为何替换 Application 需要对这 2 个类进行相关操作呢? 这就需要从安卓 Application 的启动流程来找答案了.
替换 Application 过程中涉及到的 Applicaiton 启动流程细节
我们都知道当我们在桌面上点击 app 图标后, 安卓的 Zygote 进程会 fork 一个新进程作为 app 的进程空间, 然后调用经过一系列的复杂流程后进入到 ActivityThread 的 main 函数, 因为安卓是用 java 语言开发的, 在 java 程序中程序的入口即使 main 函数, 安卓也不例外, 安卓的入口 main 函数是 ActivityThread 的 main 函数. 我们来看下其代码. 以下源码流程分析都是基于安卓 6.0.0_r1.
- public static void main(String[] args) {
- //... 省略若干不重要细节代码
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- if (false) {
- Looper.myLooper().setMessageLogging(new
- LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- // End of event ActivityThreadMain.
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
为了更好的抓住主要逻辑, 省略了很多细节代码, 可以看到 main 函数主要做了 2 件事
创建主消息循环, 执行 loop 循环(因为这个不在分析 app 启动流程的重点中, 所以不进行过多介绍)
创建 ActivityThread 对象, 调用该对象的 attach 方法.
接下来我们跟进到 attach 函数中看下
- private void attach(boolean system) {
- sCurrentActivityThread = this;
- mSystemThread = system;
- if (!system) {
- ViewRootImpl.addFirstDrawHandler(new Runnable() {
- @Override
- public void run() {
- ensureJitEnabled();
- }
- });
- android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
- UserHandle.myUserId());
- RuntimeInit.setApplicationObject(mAppThread.asBinder());
- final IActivityManager mgr = ActivityManagerNative.getDefault();// 返回值是一个 ActivityManagerService 对象
- try {
- mgr.attachApplication(mAppThread);
- } catch (RemoteException ex) {
- // Ignore
- }
- //... 省略若干不重要细节代码
- } else {
- // Don't set application object here -- if the system crashes,
- // we can't display an alert, we just want to die die die.
- android.ddm.DdmHandleAppName.setAppName("system_process",
- UserHandle.myUserId());
- try {
- mInstrumentation = new Instrumentation();
- ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- mInitialApplication = context.mPackageInfo.makeApplication(true, null);
- mInitialApplication.onCreate();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate Application():" + e.toString(), e);
- }
- }
- //... 省略若干不重要细节代码
- }
同样的省略若干不重要细节代码, 可以看到该函数分 2 中情况进行处理, 系统 app 和非系统 app, 因为我们研究的是用户 app, 所以系统 app 这块逻辑我们忽略. 可以看到在处理用户 app 时会创建一个 ActivityManagerService 对象, 然后调用该对象的 attachApplication 方法
- final IActivityManager mgr = ActivityManagerNative.getDefault();// 返回值是一个 ActivityManagerService 对象
- try {
- mgr.attachApplication(mAppThread);
- } catch (RemoteException ex) {
- // Ignore
- }
传入进去的是 mAppThread 是 ApplicationThread 对象, 该对象是一个 binder 对象, 用来在 ActivityManagerService(以下简称 AMS)和 ActivityThread 这 2 者之间进行跨进程通信. 到这逻辑就跳转到了 AMS 中了, AMS 的 attachApplication 方法主要逻辑是调用了自己的 attachApplicationLocked 方法, 因此我们直接看下 attachApplicationLocked 方法.
- private final boolean attachApplicationLocked(IApplicationThread thread,
- int pid) {
- //... 省略若干不重要细节代码
- ProfilerInfo profilerInfo = profileFile == null ? null
: new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
- profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
- app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
- new Configuration(mConfiguration), app.compat,
- getCommonServicesLocked(app.isolated),
- mCoreSettingsObserver.getCoreSettingsLocked());
- }
可以看到该函数主要逻辑是调用了传进来的 ApplicationThread 的 bindApplication 方法. 这是一个 binder 跨进程调用, 流程逻辑重新回到 ApplicationThread 的 bindApplication 方法. 跟进到 ApplicationThread 的 bindApplication 方法, 看下逻辑:
- 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 enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
- Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
- Bundle coreSettings) {
- //... 省略若干不重要细节代码
- AppBindData 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.enableOpenGlTrace = enableOpenGlTrace;
- data.restrictedBackupMode = isRestrictedBackupMode;
- data.persistent = persistent;
- data.config = config;
- data.compatInfo = compatInfo;
- data.initProfilerInfo = profilerInfo;
- sendMessage(H.BIND_APPLICATION, data);
- }
- }
可以看到该方法的主要逻辑就是把一系列参数打包为 data, 然后通过 H 这个 Handler 以 BIND_APPLICATION 消息发送出去(这里的 H 和 ApplicationThread 一样是 ActivityThread 的内部类), 因此接下来我们看下 H 时如何处理 BIND_APPLICATION 这个消息的.
- private class H extends Handler {
- public void handleMessage(Message msg) {
- if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling:" + codeToString(msg.what));
- 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;
- }
- }
可以看到调用了 ActivityTread 的 handleBindApplication 这个方法, 该方法时 Application 启动流程最核心的方法, 跟进该方法:
- /**
- * 处理 Application 启动逻辑的核心方法
- * @param data
- */
- private void handleBindApplication(AppBindData data) {
- //... 省略若干不重要细节代码
- mInstrumentationPackageName = ii.packageName;
- mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationSplitAppDirs = ii.splitSourceDirs;
- mInstrumentationLibDir = ii.nativeLibraryDir;
- mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
- mInstrumentedLibDir = data.info.getLibDir();
- ApplicationInfo instrApp = new ApplicationInfo();
- instrApp.packageName = ii.packageName;
- instrApp.sourceDir = ii.sourceDir;
- instrApp.publicSourceDir = ii.publicSourceDir;
- instrApp.splitSourceDirs = ii.splitSourceDirs;
- instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
- instrApp.dataDir = ii.dataDir;
- instrApp.nativeLibraryDir = ii.nativeLibraryDir;
- LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
- appContext.getClassLoader(), false, true, false);
- // 创建 app 运行时的上下文环境
- ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
- try {
- java.lang.ClassLoader cl = instrContext.getClassLoader();
- mInstrumentation = (Instrumentation)
- cl.loadClass(data.instrumentationName.getClassName()).newInstance();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate instrumentation"
- + data.instrumentationName + ":" + e.toString(), e);
- }
- mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
- data.instrumentationUiAutomationConnection);
- if (mProfiler.profileFile != null && !ii.handleProfiling
- && mProfiler.profileFd == null) {
- mProfiler.handlingProfiling = true;
- File file = new File(mProfiler.profileFile);
- file.getParentFile().mkdirs();
- Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
- }
- else {
- mInstrumentation = new Instrumentation();
- }
- //... 省略若干不重要细节代码
- try {
- // If the app is being launched for full backup or restore, bring it up in
- // a restricted environment with the base application class.
- // 创建 Application 对象, data.info 是一个 LoadedApk 对象
- // 也就是说会设置 LoadedApk.mApplication
- Application app = data.info.makeApplication(data.restrictedBackupMode, null);
- mInitialApplication = app;
- // don't bring up providers in restricted mode; they may depend on the
- // app's custom Application class
- if (!data.restrictedBackupMode) {
- // 如果存在 ContentProvider, 先加载 ContentProvider
- List<ProviderInfo> providers = data.providers;
- if (providers != null) {
- installContentProviders(app, providers);
- // For process that contains content providers, we want to
- // ensure that the JIT is enabled "at some point".
- mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
- }
- }
- // Do this after providers, since instrumentation tests generally start their
- // test thread at this point, and we don't want that racing.
- try {
- //Instrumentation 的 onCreate 是一个空实现
- mInstrumentation.onCreate(data.instrumentationArgs);
- }
- catch (Exception e) {
- throw new RuntimeException(
- "Exception thrown in onCreate() of"
- + data.instrumentationName + ":" + e.toString(), e);
- }
- try {
- // 调用 Application 的 onCreate 函数
- mInstrumentation.callApplicationOnCreate(app);
- } catch (Exception e) {
- if (!mInstrumentation.onException(app, e)) {
- throw new RuntimeException(
- "Unable to create application" + app.getClass().getName()
- + ":" + e.toString(), e);
- }
- }
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
- }
- }
可以看到该方法主要做了以下几件事:
创建 app 运行时的上下文环境 ContextImpl
调用 LoadedApk 的 makeApplication 方法创建 Application 对象, 同时将该对象赋值给 ActivityThread 的 mInitialApplication 成员变量, 同时需要注意的是 LoadedApk 的 makeApplication 方法会把创建的 Application 对象赋值给自己内部定义的 mApplication 成员变量
如果存在 ContentProvider, 先加载 ContentProvider
通过 Instrumentationd 的 callApplicationOnCreate 函数间接调用当前 Application 的 onCreate 函数, 至此 Application 启动流程就跑起来了.
其中 LoadedApk 的 makeApplication 方法逻辑如下:
- public Application makeApplication(boolean forceDefaultAppClass,
- Instrumentation instrumentation) {
- if (mApplication != null) {
- return mApplication;
- }
- Application app = null;
- String appClass = mApplicationInfo.className;
- if (forceDefaultAppClass || (appClass == null)) {
- appClass = "android.app.Application";
- }
- try {
- java.lang.ClassLoader cl = getClassLoader();
- if (!mPackageName.equals("android")) {
- initializeJavaContextClassLoader();
- }
- ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
- // 创建 Application 对象
- app = mActivityThread.mInstrumentation.newApplication(
- cl, appClass, appContext);
- appContext.setOuterContext(app);
- } catch (Exception e) {
- if (!mActivityThread.mInstrumentation.onException(app, e)) {
- throw new RuntimeException(
- "Unable to instantiate application" + appClass
- + ":" + e.toString(), e);
- }
- }
- // 将创建的 Application 对象添加到 ActivityThread 的 mAllApplications 中
- mActivityThread.mAllApplications.add(app);
- // 将创建的 Applicaiotn 对象赋值给自己的成员变量 mApplication
- mApplication = app;
- if (instrumentation != null) {
- try {
- instrumentation.callApplicationOnCreate(app);
- } catch (Exception e) {
- if (!instrumentation.onException(app, e)) {
- throw new RuntimeException(
- "Unable to create application" + app.getClass().getName()
- + ":" + e.toString(), e);
- }
- }
- }
- //... 省略若干不重要细节代码
- return app;
- }
可以看到将创建的 Application 对象添加进了 ActivityThread 的 mAllApplications 中, 同时也将其赋值给了自己的成员变量 mApplication.
现在回过头来看 Instan Run 替换 Application 的过程是不是就非常清晰明朗了, 而且从这个过程中看到安卓中的四大组件以及 Application 之所以不能够像 java 那样直接 new 一个就可以用是因为涉及到一系列复杂的流程初始化, 会在 framework 层来回牵扯, 互相赋值, 直接 new 的四大组件和 Application 对象没有这些赋值过程, 因此不具备生命周期, 上下文环境等一些安卓上特有的属性, 另外也可以看到 ContenProvider 的执行是在 Applicaton 的 onCreate 之前.
总结
先总结下 Application 的启动流程的主要逻辑
- ActivityThread#main->ActivityThread#attach->ActivityManagerService#attachApplication, 此时流程逻辑跳转到了 AMS
- AMS#attachApplication->ActivityManagerService#attachApplicationLocked->
- ApplicationThread#bindApplication, 此时 AMS 通过传进来的 ApplicationThread 进行 binder 通信调用 ApplicationThread 的 bindApplication, 此时逻辑重新回到 ActivityThread
在 bindApplication 中通过 H 这个 Handler 发送 BIND_APPLICATION 消息, 该消息被 ActivityThread#handleBindApplication 处理
handleBindApplication 是 Application 的核启动流程的核心方法, 在该方法中会直接或间接创建 LoadedApk 对象, 创建 app 上下文环境 ContextImpl, 创建 Application
再总结下替换 Application 的主要逻辑
将 ActivityThread 类的 mInitialApplication 成员替换为真实的 Application
将 ActivityThread 类的 mAllApplications 这个 ArrayList<Application > 对象的所有的子元素替换为真实的 Application
枚举所有的 LoadedApk 或者 PackageInfo 对象, 将这些对象中的 mApplication 域设置为真实的 Application. 同样也要把 Application 的 mLoadedApk 设置为该 LoadedApk 对象
打赏 http://huqi.tech/index.php/2018/04/15/instant-run_application_replace/javascript:;
来源: https://juejin.im/entry/5ae6a6db518825673123fc9a