背景
虽然热更新和 Hook 技术都被大家聊烂了,但是还是想和大家聊一下这方面的内容。最近做一些 Android 方面的优化工作,大家知道 Android 的 ClassLoader 在加载 dex 文件的过程中,而 AndroidManifest 的 Application 类就在 dex 文件中,Application 通常会做一些全局的初始化工作,在加载 dex 之前,我们需要替换原有的 Application 为 ProxyApplication。使其应用启动时加载 ProxyApplication,然后在其中实现加载 dex 等一些流程处理。而后要替换回原有的 Application(以下称为 RealApplication),确保应用正常运行,并且要保持生命周期、初始化顺序不变,屏蔽对于应用中 getContext,getApplicationContext 的影响。
在替换 Application 的过程中,应该注意以下几点:
- 创建 RealApplication,维护正常的生命周期,并进行回调。
- 对应用中屏蔽掉 ProxyApplication,对于下层无感知。在 Activity 等调用 getApplicationContext 之后,应该返回 RealApplication。
- ContentProvider 创建时机比较特殊,在满足正常的初始化顺序之后,也要屏蔽 ProxyApplication 的存在。
方案实现
在 AndroidManifest.xml 文件中替换 Application 为 ProxyApplication,可以使用自动化方式,或者打包方式,关于实现的具体细节此处不讨论。这里主要叙述创建 RealApplication 的过程。替换了 ProxyApplication 之后,对于系统而言 ProxyApplication 就是应用初始化的入口,所有的回调均是在 ProxyApplication 中发生。我们主要关注 attachBaseContext 和 onCreate 的回调。
创建 RealApplication
创建 RealApplication,我们可以使用反射的方式 newInstance 创建对象,然后执行回调 attachBaseContext。但是对于不同的系统版本,内部执行的细节可能不同,或者有其它相关逻辑的处理,所以我们采用另一种方式进行处理。首先看系统源码的如何实现,这里选择 8.0.0 的系统源码进行分析,其它版本去 http://androidxref.com 查看。
我们知道,Android 初始化是从 android.app.ActivityThread 开始的,所以从 ActivityThread 开始查看,ActivityThread 中存在静态方法 currentActivityThread 返回实例。可以参考系统的 ActivityThread 类:
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
ActivityThread 内部存在成员变量 AppBindData mBoundApplication。AppBindData 是一个静态内部类,其中包含成员变量 LoadedApk info。查看 android.app.LoadedApk 源代码,发现创建 Application 的 makeApplication 方法。
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = ;
mApplicationInfo.className;
(forceDefaultAppClass || (appClass == )) {
appClass = ;
}
{
java.lang. getClassLoader();
(!mPackageName.equals()) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
);
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
ContextImpl.createAppContext(mActivityThread, );
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} (Exception e) {
(!mActivityThread.mInstrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
(
+ appClass
+ + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
(instrumentation != ) {
{
instrumentation.callApplicationOnCreate(app);
} (Exception e) {
(!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
(
+ app.getClass().getName()
+ + e.toString(), e);
}
}
}
SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
packageIdentifiers.size();
( ; i < N; i++) {
packageIdentifiers.keyAt(i);
(id == || id == ) {
;
}
rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
app;
}


