/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.fd.runtime;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Application;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.os.Process;
import android.util.Log;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.logging.Level;
// This is based on the reflection parts of
// com.google.devtools.build.android.incrementaldeployment.StubApplication,
// plus changes to compile on JDK 6.
//
// (The code to handle resource loading etc is different; see FileManager.)
//
// The original is
// https://cs.corp.google.com/codesearch/f/piper///depot/google3/third_party/bazel/src/tools/android/java/com/google/devtools/build/android/incrementaldeployment/StubApplication.java?cl=93287264
// Public (May 11 revision, ca96e11)
// https://github.com/google/bazel/blob/master/src/tools/android/java/com/google/devtools/build/android/incrementaldeployment/StubApplication.java
/**
* A stub application that patches the class loader, then replaces itself with the real application
* by applying a liberal amount of reflection on Android internals.
* <p>
* <p>This is, of course, terribly error-prone. Most of this code was tested with API versions
* 8, 10, 14, 15, 16, 17, 18, 19 and 21 on the Android emulator, a Nexus 5 running Lollipop LRX22C
* and a Samsung GT-I5800 running Froyo XWJPE. The exception is {@code monkeyPatchAssetManagers},
* which only works on Kitkat and Lollipop.
* <p>
* <p>Note that due to a bug in Dalvik, this only works on Kitkat if ART is the Java runtime.
* <p>
* <p>Unfortunately, if this does not work, we don't have a fallback mechanism: as soon as we
* build the APK with this class as the Application, we are committed to going through with it.
* <p>
* <p>This class should use as few other classes as possible before the class loader is patched
* because any class loaded before it cannot be incrementally deployed.
*
* 1. createResources: 创建资源 - 本质就是 copy resources.ap_
* - 1.1 /data/data/.../files/instant-run/inbox/resources.ap_ 是否存在
* - 1.2 存在的话,复制到 /data/data/.../files/instant-run/left(or right)/resources.ap_ 下
* - 1.3 判断是否 复制成功,即有文件
* - 1.4 判断 2. 路径下的 resources.ap_ 是否没被修改( 0L = 不存在 ),并且如果资源文件的修改时间
* - 小于 APP 的 APK 修改时间的话,那么说明这是一个 旧的资源文件( 失效的旧的 resources.ap_ ),应该
* - 忽略( externalResourcePath = null )
* 2. setupClassLoaders: HOOK BootstrapApplication 的 ClassLoader 的 类加载机制
* - 2.1 获取 /data/data/.../files/instant-run/dex 下的所有 .dex 路径( List )
* - 2.2 如果有 dex 路径 List 没有内容,直接 return
* - 2.3 获取加载 BootstrapApplication 的 ClassLoader
* - 2.4 反射该 ClassLoader 的 getLdLibraryPath 方法拿到 nativeLibraryPath
* - 2.4.1 如果成功,则直接复制给 nativeLibraryPath
* - 2.4.2 如果失败,捕获异常,打印 Log 后,设置 nativeLibraryPath = /data/data/.../lib
* - 2.5 调用了 静态方法 IncrementalClassLoader.inject(....) 后,直接 HOOK 了 该 ClassLoader 的
* - 加载模式为:BootClassLoader -> incrementalClassLoader -> classLoader
* - 2.6 这样的话 BootstrapApplication 的 ClassLoader 的加载 Class 机制就会先走插件 incrementalClassLoader
* 3. createRealApplication: 创建 真正的 Application( App 内自定义的 Application )
* - 3.1 AppInfo 中取出,真正 Application 的 packageName,forName(...) 实例化一个 Class<? extends
* - Application>
* - 3.2 反射这个 真正 Application 默认构造方法
* - 3.3 然后在调用这个默认的构造方法去创建这个 真正 Application
* - 3.4 最后保存 真正 Application 实例 在 realApplication 上
* 4. attachBaseContext: BootstrapApplication 的 attachBaseContext 方法
* - 4.1 MultiDex 处理
* - 4.1.1 判断是否使用了 MultiDex
* - 4.1.2 拿到 App 的 APK 完整路径
* - 4.1.3 判断该 APP 的 APK 完整路径是否有最后一次 修改时间,记录为 apkModified
* - 4.1.4 用 apkModified ( 0L = 不存在 ) 标记,去调用 createResources(...) 方法
* - 4.1.5 创建资源文件 resources.ap_,实质上是 将 /data/data/.../files/instant-run/inbox/resources.ap_
* - 复制到 /data/data/.../files/instant-run/left(or right)/resources.ap_
* - 4.1.6 /data/data/(app package name)/cache 和 apkModified 作为参数调用 setupClassLoaders(...)方法
* - 4.1.7 HOOK BootstrapApplication 的 ClassLoader 的 类加载机制
* - 4.2 代理 realApplication.attachBaseContext(...)
* - 4.2.1 创建 真正的 Application( App 内自定义的 Application ),保存在 realApplication
* - 4.2.2 调用 super.attachBaseContext(context)
* - 4.2.3 然后 反射 realApplication 的 attachBaseContext 方法,调用 realApplication 的
* - attachBaseContext 方法,达到了 BootstrapApplication 代理了
* - realApplication.attachBaseContext(...) 的效果
* 5. onCreate: BootstrapApplication 的 onCreate 方法
* - 5.1 MultiDex 处理
* - 5.1.1 判断是否使用了 MultiDex
* - 5.1.2 如果没有使用 MultiDex
* - 5.1.2.1 MonkeyPatcher.monkeyPatchApplication(...) 去 hook 掉整个 ActivityThread 内
* - ( 包括 ActivityThread 内所有 LoadedApk 内部的 BootstrapApplication )所有
* - 关于 BootstrapApplication 的地方,替换为 RealApplication
* - 5.1.2.2 MonkeyPatcher.monkeyPatchExistingResources(...) 反射调用
* - AssetManager.addAssetPath 方法加载 补丁资源,得到一个 补丁 AssetManager。然后
* - Hook 掉 Resource, ResourcesImpl 内部的所有( 包括内部的 Theme or ThemeImpl )的
* - mAssets。如果 < 7.0, 先 Hook AssetManager 的 createTheme 方法去创建一个 补丁 Theme
* - 然后 Hook Activity 的 Theme 的 mTheme Field 为 补丁 Theme
* - 最后,pruneResourceCaches(@NonNull Object resources) 方法去删除 资源缓存
* - 5.1.3 如果使用 MultiDex 了,就不执行 5.1.2.1,只执行 5.1.2.2
* - 5.2 启动 Server 和 代理 RealApplication 的 onCreate 方法
* - 5.2.1 判断 AppInfo.applicationId( applicationId = packageName ) != null
* - 5.2.1.1 如果 AppInfo.applicationId == null,不执行以下操作,跳过这个 if
* - 5.2.1.2 如果 AppInfo.applicationId != null,继续
* - 5.2.2 获取 ActivityManager,拿到手机内所有进程的信息( RunningAppProcessInfo )
* - 5.2.3 寻找该 App 的对应的 RunningAppProcessInfo 信息
* - 5.2.4 然后 Server.create(...) 创建 Server 的同时,会在 Server 的构造方法中启动 Server
* - 5.2.5 代理 RealApplication 的 onCreate 方法
*/
public class BootstrapApplication extends Application {
public static final String LOG_TAG = "InstantRun";
/**
* 静态代码块实例化 Logging
*/
static {
com.android.tools.fd.common.Log.logging =
new com.android.tools.fd.common.Log.Logging() {
@Override
public void log(@NonNull Level level, @NonNull String string) {
log(level, string, null /* throwable */);
}
@Override
public boolean isLoggable(@NonNull Level level) {
if (level == Level.SEVERE) {
return Log.isLoggable(LOG_TAG, Log.ERROR);
} else if (level == Level.FINE) {
return Log.isLoggable(LOG_TAG, Log.VERBOSE);
} else {
return Log.isLoggable(LOG_TAG, Log.INFO);
}
}
@Override
public void log(@NonNull Level level, @NonNull String string,
@Nullable Throwable throwable) {
if (level == Level.SEVERE) {
if (throwable == null) {
Log.e(LOG_TAG, string);
} else {
Log.e(LOG_TAG, string, throwable);
}
} else if (level == Level.FINE) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
if (throwable == null) {
Log.v(LOG_TAG, string);
} else {
Log.v(LOG_TAG, string, throwable);
}
}
} else if (Log.isLoggable(LOG_TAG, Log.INFO)) {
if (throwable == null) {
Log.i(LOG_TAG, string);
} else {
Log.i(LOG_TAG, string, throwable);
}
}
}
};
}
private String externalResourcePath;
private Application realApplication;
public BootstrapApplication() {
// always log such that we can debug issues like http://b.android.com/215805
Log.i(LOG_TAG, String.format(
"Instant Run Runtime started. Android package is %s, real application class is %s.",
AppInfo.applicationId, AppInfo.applicationClass));
}
/**
* 创建资源 - 本质就是 copy resources.ap_
*
* 1. /data/data/.../files/instant-run/inbox/resources.ap_ 是否存在
* 2. 存在的话,复制到 /data/data/.../files/instant-run/left(or right)/resources.ap_ 下
* 3. 判断是否 复制成功,即有文件
* 4. 判断 2. 路径下的 resources.ap_ 是否没被修改( 0L = 不存在 ),并且如果资源文件的修改时间
* - 小于 APP 的 APK 修改时间的话,那么说明这是一个 旧的资源文件( 失效的旧的 resources.ap_ ),应该
* - 忽略( externalResourcePath = null )
*
* @param apkModified apk 是否被修改过,0L 表示没修改( 0L = 不存在 ),其他表示修改
*/
private void createResources(long apkModified) {
// Look for changes stashed in the inbox folder while the server was not running
FileManager.checkInbox();
File file = FileManager.getExternalResourceFile();
externalResourcePath = file != null ? file.getPath() : null;
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Resource override is " + externalResourcePath);
}
if (file != null) {
try {
long resourceModified = file.lastModified();
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Resource patch last modified: " + resourceModified);
Log.v(LOG_TAG, "APK last modified: " + apkModified + " " +
(apkModified > resourceModified ? ">" : "<") + " resource patch");
}
if (apkModified == 0L || resourceModified <= apkModified) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Ignoring resource file, older than APK");
}
externalResourcePath = null;
}
} catch (Throwable t) {
Log.e(LOG_TAG, "Failed to check patch timestamps", t);
}
}
}
/**
* HOOK BootstrapApplication 的 ClassLoader 的 类加载机制
*
* 1. 获取 /data/data/.../files/instant-run/dex 下的所有 .dex 路径( List )
* 2. 如果有 dex 路径 List 没有内容,直接 return
* 3. 获取加载 BootstrapApplication 的 ClassLoader
* 4. 反射该 ClassLoader 的 getLdLibraryPath 方法拿到 nativeLibraryPath
* - 4.1 如果成功,则直接复制给 nativeLibraryPath
* - 4.2 如果失败,捕获异常,打印 Log 后,设置 nativeLibraryPath = /data/data/.../lib
* 5. 调用了 静态方法 IncrementalClassLoader.inject(....) 后,直接 HOOK 了 该 ClassLoader 的
* - 加载模式为:BootClassLoader -> incrementalClassLoader -> classLoader
* 6. 这样的话 BootstrapApplication 的 ClassLoader 的加载 Class 机制就会先走插件 incrementalClassLoader
*
* @param context context
* @param codeCacheDir /data/data/(app package name)/cache
* @param apkModified 该 APP 的 APK 完整路径是否有最后一次 修改时间,记录为 apkModified ( 0L = 不存在 )
*/
private static void setupClassLoaders(Context context, String codeCacheDir, long apkModified) {
List<String> dexList = FileManager.getDexList(context, apkModified);
// Make sure class loader finds these
@SuppressWarnings("unused") Class<Server> server = Server.class;
@SuppressWarnings("unused") Class<MonkeyPatcher> patcher = MonkeyPatcher.class;
if (!dexList.isEmpty()) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Bootstrapping class loader with dex list " + join('\n', dexList));
}
ClassLoader classLoader = BootstrapApplication.class.getClassLoader();
String nativeLibraryPath;
try {
nativeLibraryPath = (String) classLoader.getClass().getMethod("getLdLibraryPath")
.invoke(classLoader);
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Native library path: " + nativeLibraryPath);
}
} catch (Throwable t) {
Log.e(LOG_TAG, "Failed to determine native library path " + t.getMessage());
nativeLibraryPath = FileManager.getNativeLibraryFolder().getPath();
}
IncrementalClassLoader.inject(
classLoader,
nativeLibraryPath,
codeCacheDir,
dexList);
} else {
Log.w(LOG_TAG, "No instant run dex files added to classpath");
}
}
/**
* 拼接 一组 dex 的路径 的 String 内容
*
* @param on 间隔符
* @param list 一组 dex 的路径
* @return String
*/
public static String join(char on, List<String> list) {
StringBuilder stringBuilder = new StringBuilder();
for (String item : list) {
stringBuilder.append(item).append(on);
}
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
return stringBuilder.toString();
}
/**
* 创建 真正的 Application( App 内自定义的 Application )
*
* 1. AppInfo 中取出,真正 Application 的 packageName,forName(...) 实例化一个 Class<? extends Application>
* 2. 反射这个 真正 Application 默认构造方法
* 3. 然后在调用这个默认的构造方法去创建这个 真正 Application
* 4. 最后保存 真正 Application 实例 在 realApplication 上
*/
private void createRealApplication() {
if (AppInfo.applicationClass != null) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "About to create real application of class name = " +
AppInfo.applicationClass);
}
try {
@SuppressWarnings("unchecked")
Class<? extends Application> realClass =
(Class<? extends Application>) Class.forName(AppInfo.applicationClass);
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Created delegate app class successfully : " + realClass +
" with class loader " + realClass.getClassLoader());
}
Constructor<? extends Application> constructor = realClass.getConstructor();
realApplication = constructor.newInstance();
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Created real app instance successfully :" + realApplication);
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
} else {
realApplication = new Application();
}
}
/**
* BootstrapApplication 的 attachBaseContext 方法
*
* 1. MultiDex 处理
* - 1.1 判断是否使用了 MultiDex
* - 1.2 拿到 App 的 APK 完整路径
* - 1.3 判断该 APP 的 APK 完整路径是否有最后一次 修改时间,记录为 apkModified
* - 1.4 用 apkModified ( 0L = 不存在 ) 标记,去调用 createResources(...) 方法
* - 1.5 创建资源文件 resources.ap_,实质上是 将 /data/data/.../files/instant-run/inbox/resources.ap_
* - 复制到 /data/data/.../files/instant-run/left(or right)/resources.ap_
* - 1.6 /data/data/(app package name)/cache 和 apkModified 作为参数调用 setupClassLoaders(...) 方法
* - 1.7 HOOK BootstrapApplication 的 ClassLoader 的 类加载机制
*
* 2. 代理 realApplication.attachBaseContext(...)
* - 2.1 创建 真正的 Application( App 内自定义的 Application ),保存在 realApplication
* - 2.2 调用 super.attachBaseContext(context)
* - 2.3 然后 反射 realApplication 的 attachBaseContext 方法,调用 realApplication 的 attachBaseContext
* - 方法,达到了 BootstrapApplication 代理了 realApplication.attachBaseContext(...) 的效果
*
* @param context BootstrapApplication 的 ContextImpl
*/
@Override
protected void attachBaseContext(Context context) {
// As of Marshmallow, we use APK splits and don't need to rely on
// reflection to inject classes and resources for coldswap
//noinspection PointlessBooleanExpression
/**
* Step 1 MultiDex 处理
*
* 1. 判断是否使用了 MultiDex
* 2. 如果使用了 MultiDex,不处理以下流程
* 3. 拿到 App 的 APK 完整路径
* 4. 判断该 APP 的 APK 完整路径是否有最后一次 修改时间,记录为 apkModified
* 5. 用 apkModified ( 0L = 不存在 ) 标记,去调用 createResources(...) 方法
* 6. 创建资源文件 resources.ap_,实质上是 将 /data/data/.../files/instant-run/inbox/resources.ap_
* - 复制到 /data/data/.../files/instant-run/left(or right)/resources.ap_
* 7. /data/data/(app package name)/cache 和 apkModified 作为参数调用 setupClassLoaders(...) 方法
* 8. HOOK BootstrapApplication 的 ClassLoader 的 类加载机制
*/
if (!AppInfo.usingApkSplits) {
String apkFile = context.getApplicationInfo().sourceDir;
long apkModified = apkFile != null ? new File(apkFile).lastModified() : 0L;
createResources(apkModified);
setupClassLoaders(context, context.getCacheDir().getPath(), apkModified);
}
/**
* Step 2 代理 realApplication.attachBaseContext(...)
*
* 1. 创建 真正的 Application( App 内自定义的 Application ),保存在 realApplication
* 2. 调用 super.attachBaseContext(context)
* 3. 然后 反射 realApplication 的 attachBaseContext 方法,调用 realApplication 的 attachBaseContext 方法
* - 达到了 BootstrapApplication 代理了 realApplication.attachBaseContext(...) 的效果
*/
createRealApplication();
// This is called from ActivityThread#handleBindApplication() -> LoadedApk#makeApplication().
// Application#mApplication is changed right after this call, so we cannot do the monkey
// patching here. So just forward this method to the real Application instance.
super.attachBaseContext(context);
if (realApplication != null) {
try {
Method attachBaseContext =
ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
attachBaseContext.setAccessible(true);
attachBaseContext.invoke(realApplication, context);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
/**
* 代理 RealApplication 的 createPackageContext(...) 方法
*
* @param packageName packageName
* @param flags flags
* @return Context
* @throws PackageManager.NameNotFoundException
*/
@Override
public Context createPackageContext(String packageName, int flags)
throws PackageManager.NameNotFoundException {
Context c = realApplication.createPackageContext(packageName, flags);
return c == null ? realApplication : c;
}
/**
* 代理 RealApplication 的 registerComponentCallbacks(...) 方法
*
* @param callback callback
*/
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
realApplication.registerComponentCallbacks(callback);
}
/**
* 代理 RealApplication 的 registerActivityLifecycleCallbacks(...) 方法
*
* @param callback callback
*/
@Override
public void registerActivityLifecycleCallbacks(
ActivityLifecycleCallbacks callback) {
realApplication.registerActivityLifecycleCallbacks(callback);
}
/**
* 代理 RealApplication 的 registerOnProvideAssistDataListener(...) 方法
*
* @param callback callback
*/
@Override
public void registerOnProvideAssistDataListener(
OnProvideAssistDataListener callback) {
realApplication.registerOnProvideAssistDataListener(callback);
}
/**
* 代理 RealApplication 的 unregisterComponentCallbacks(...) 方法
*
* @param callback callback
*/
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
realApplication.unregisterComponentCallbacks(callback);
}
/**
* 代理 RealApplication 的 unregisterActivityLifecycleCallbacks(...) 方法
*
* @param callback callback
*/
@Override
public void unregisterActivityLifecycleCallbacks(
ActivityLifecycleCallbacks callback) {
realApplication.unregisterActivityLifecycleCallbacks(callback);
}
/**
* 代理 RealApplication 的 unregisterOnProvideAssistDataListener(...) 方法
*
* @param callback callback
*/
@Override
public void unregisterOnProvideAssistDataListener(
OnProvideAssistDataListener callback) {
realApplication.unregisterOnProvideAssistDataListener(callback);
}
/**
* BootstrapApplication 的 onCreate 方法
*
* 1. MultiDex 处理
* - 1.1 判断是否使用了 MultiDex
* - 1.2 如果没有使用 MultiDex
* - 1.2.1 MonkeyPatcher.monkeyPatchApplication(...) 去 hook 掉整个 ActivityThread 内
* - ( 包括 ActivityThread 内所有 LoadedApk 内部的 BootstrapApplication )所有
* - 关于 BootstrapApplication 的地方,替换为 RealApplication
* - 1.2.2 MonkeyPatcher.monkeyPatchExistingResources(...) 反射调用
* - AssetManager.addAssetPath 方法加载 补丁资源,得到一个 补丁 AssetManager。然后
* - Hook 掉 Resource, ResourcesImpl 内部的所有( 包括内部的 Theme or ThemeImpl )的
* - mAssets。如果 < 7.0, 先 Hook AssetManager 的 createTheme 方法去创建一个 补丁 Theme
* - 然后 Hook Activity 的 Theme 的 mTheme Field 为 补丁 Theme
* - 最后,pruneResourceCaches(@NonNull Object resources) 方法去删除 资源缓存
* - 1.3 如果使用 MultiDex 了,就不执行 1.2.2,只执行 1.2.1
*
* 2. 启动 Server 和 代理 RealApplication 的 onCreate 方法
* - 2.1 判断 AppInfo.applicationId( applicationId = packageName ) != null
* - 2.1.1 如果 AppInfo.applicationId == null,不执行以下操作,跳过这个 if
* - 2.1.2 如果 AppInfo.applicationId != null,继续
* - 2.2 获取 ActivityManager,拿到手机内所有进程的信息( RunningAppProcessInfo )
* - 2.3 寻找该 App 的对应的 RunningAppProcessInfo 信息
* - 2.4 然后 Server.create(...) 创建 Server 的同时,会在 Server 的构造方法中启动 Server
* - 2.5 代理 RealApplication 的 onCreate 方法
*/
@Override
public void onCreate() {
/**
* Step 1 MultiDex 处理
*
* 1. 判断是否使用了 MultiDex
* 2. 如果没有使用 MultiDex
* - 2.1 MonkeyPatcher.monkeyPatchApplication(...) 去 hook 掉整个 ActivityThread 内
* - ( 包括 ActivityThread 内所有 LoadedApk 内部的 BootstrapApplication )所有
* - 关于 BootstrapApplication 的地方,替换为 RealApplication
* - 2.2 MonkeyPatcher.monkeyPatchExistingResources(...) 反射调用 AssetManager.addAssetPath 方法
* - 加载 补丁资源,得到一个 补丁 AssetManager。然后 Hook 掉 Resource, ResourcesImpl 内
* - 部的所有( 包括内部的 Theme or ThemeImpl )的 mAssets。如果 < 7.0, 先 Hook AssetManager
* - 的 createTheme 方法去创建一个 补丁 Theme,然后 Hook Activity 的 Theme 的 mTheme Field 为 补丁 Theme。
* - 最后,pruneResourceCaches(@NonNull Object resources) 方法去删除 资源缓存
* 3. 如果使用 MultiDex 了,就不执行 2.2,只执行 2.1
*/
// As of Marshmallow, we use APK splits and don't need to rely on
// reflection to inject classes and resources for coldswap
//noinspection PointlessBooleanExpression
if (!AppInfo.usingApkSplits) {
MonkeyPatcher.monkeyPatchApplication(
BootstrapApplication.this, BootstrapApplication.this,
realApplication, externalResourcePath);
MonkeyPatcher.monkeyPatchExistingResources(BootstrapApplication.this,
externalResourcePath, null);
} else {
// We still need to set the application instance in the LoadedApk etc
// such that getApplication() returns the new application
MonkeyPatcher.monkeyPatchApplication(
BootstrapApplication.this, BootstrapApplication.this,
realApplication, null);
}
super.onCreate();
/**
* Step 2 启动 Server 和 代理 RealApplication 的 onCreate 方法
*
* 1. 判断 AppInfo.applicationId( applicationId = packageName ) != null
* - 1.1 如果 AppInfo.applicationId == null,不执行以下操作,跳过这个 if
* - 1.2 如果 AppInfo.applicationId != null,继续
* 2. 获取 ActivityManager,拿到手机内所有进程的信息( RunningAppProcessInfo )
* 3. 寻找该 App 的对应的 RunningAppProcessInfo 信息
* 4. 然后 Server.create(...) 创建 Server 的同时,会在 Server 的构造方法中启动 Server
* 5. 代理 RealApplication 的 onCreate 方法
*/
// Start server, unless we're in a multiprocess scenario and this isn't the
// primary process
if (AppInfo.applicationId != null) {
try {
boolean foundPackage = false;
int pid = Process.myPid();
ActivityManager manager = (ActivityManager) getSystemService(
Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
boolean startServer;
if (processes != null && processes.size() > 1) {
// Multiple processes: look at each, and if the process name matches
// the package name (for the current pid), it's the main process.
startServer = false;
for (RunningAppProcessInfo processInfo : processes) {
if (AppInfo.applicationId.equals(processInfo.processName)) {
foundPackage = true;
if (processInfo.pid == pid) {
startServer = true;
break;
}
}
}
if (!startServer && !foundPackage) {
// Safety check: If for some reason we didn't even find the main package,
// start the server anyway. This safeguards against apps doing strange
// things with the process name.
startServer = true;
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Multiprocess but didn't find process with package: "
+ "starting server anyway");
}
}
} else {
// If there is only one process, start the server.
startServer = true;
}
if (startServer) {
Server.create(AppInfo.applicationId, BootstrapApplication.this);
}
} catch (Throwable t) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Failed during multi process check", t);
}
Server.create(AppInfo.applicationId, BootstrapApplication.this);
}
}
if (realApplication != null) {
realApplication.onCreate();
}
}
}