一.原理
ActivityThread类中有一个内部类H继承自Handler,当启动Activity时,H会发送LAUNCH_ACTIVITY消息,从而执行到H的handleMessage方法,LAUNCH_ACTIVITY事件的处理如下:
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
在以上方法中,通过getPackageInfoNoCheck得到LoadedAPK对象,getPackageInfoNoCheck首先在mPackages中取LoadedAPK,如果取不到,则创建一个LoadedAPK。getPackageInfoNoCheck方法如下:
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,ClassLoader baseLoader, boolean securityViolation, boolean includeCode,boolean registerPackage) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser) {
ref = null;
} else if (includeCode) {
ref = 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())) {
...
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
...
if (differentUser) {
} else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
return packageInfo;
}
}
在H的handleMessage中,拿到LoadedAPK后,会执行handleLaunchActivity->performLaunchActivity,在performLaunchActivity中,会通过LoadedAPK拿到类加载器,加载Activity:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
}
宿主中LoadedAPK的类加载器只能用于加载宿主中的class,如果要加载插件中的class,需要自定义类加载器和对应的LoadedAPK放入缓存中,通过插件包名取到插件的LoadedAPK,进行插件的类加载。
二.实现
1.拿到ActivityThread和mPackages对象
ActivityThread中存放LoadedAPK的缓存是mPackages,首先获得ActivityThread和mPackages:
Class mActivityThreadClass = Class.forName("android.app.ActivityThread");
Object mActivityThread = mActivityThreadClass.getMethod("currentActivityThread").invoke(null);
Field mPackagesField = mActivityThreadClass.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
Map mPackages = (Map) mPackagesField.get(mActivityThread);
2.拿到LoadedAPK
通过getPackageInfoNoCheck方法拿到LoadedAPK。getPackageInfoNoCheck方法有两个参数:ApplicationInfo ai和CompatibilityInfo compatInfo,首先获取ApplicationInfo对象,通过执行PackageParser类的generateApplicationInfo(Package p, int flags,PackageUserState state)方法得到:
Class mPackageParserClass = Class.forName("android.content.pm.PackageParser");
Object mPackageParser = mPackageParserClass.newInstance();
Method mApplicationInfoMethod =mPackageParserClass.getMethod("generateApplicationInfo",$PackageClass,
int.class, mPackageUserStateClass);
ApplicationInfo applicationInfo = (ApplicationInfo)mApplicationInfoMethod.invoke(mPackageParser, mPackage, 0, mPackageUserStateClass.newInstance());
获取CompatibilityInfo compatInfo:
Class mCompatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
Field default_compatibility_infoField = mCompatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
default_compatibility_infoField.setAccessible(true);
Object mCompatibilityInfo = default_compatibility_infoField.get(null);
通过getPackageInfoNoCheck获取LoadedAPK
Method getPackageInfoNoCheckMethod = mActivityThreadClass.getMethod("getPackageInfoNoCheck", ApplicationInfo.class, mCompatibilityInfoClass);
Object loadedApk = getPackageInfoNoCheckMethod.invoke(mActivityThread, applicationInfo, mCompatibilityInfo);
3.替换LoadedAPK的类加载器并加入缓存
ClassLoader classLoader = new PluginClassLoader(file.getAbsolutePath(), pDir.getAbsolutePath(),null,getClassLoader());
Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
mClassLoaderField.set(loadedApk,classLoader);
WeakReference weakReference = new WeakReference(loadedApk);
mPackages.put(applicationInfo.packageName,weakReference);
除了以上操作外,依然要首先执行Hook式插件化中的前两步绕过AMS检查和使用目标Intent替换掉代理Intent。相比于Hook式插件化,去掉了宿主和插件的dexElements元素合并至宿主的过程,使用插件LoadedAPK直接加载插件的类。