PluginManager.getInstance(base).init();
保证单例
private static volatile PluginManager sInstance = null;
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null)
sInstance = new PluginManager(base);
}
}
return sInstance;
}
private PluginManager(Context context) {
Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
} else {
this.mContext = ((Application)app).getBaseContext();
}
prepare();
}
下面仔细按照这个方法的步骤分析初始化过程做了哪些事情
private void prepare() {
Systems.sHostContext = getHostContext();
this.hookInstrumentationAndHandler();//对Instrumentation和H类进行hook
this.hookSystemServices();//对AMS代理也就是AMP对象进行hook
hookDataBindingUtil();
}
private void hookInstrumentationAndHandler() {
try {
Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);//[1]从Context开始hook
if (baseInstrumentation.getClass().getName().contains("lbe")) {
System.exit(0);
}
//[2]
final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
Object activityThread = ReflectUtil.getActivityThread(this.mContext);
ReflectUtil.setInstrumentation(activityThread, instrumentation);
ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
e.printStackTrace();
}
}
其中getActivityThread这个方法中对ActivityThread进行了获取(通过反射),然后从ActivityThread中通过getInstrumentation方法得到Instrumentation对象,这个对象会跟踪应用内application和activity生命周期,该对象的创建在ActivityThread::handleBindApplication函数中:
if (data.instrumentationName !=null) {
...
java.lang.ClassLoadercl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();
...
} else {
mInstrumentation =newInstrumentation();
}
[1]
public static Instrumentation getInstrumentation(Context base) {
if (getActivityThread(base) != null) {
try {
sInstrumentation = (Instrumentation) ReflectUtil.invoke(
sActivityThread.getClass(), sActivityThread, "getInstrumentation");
} catch (Exception e) {
e.printStackTrace();
}
}
return sInstrumentation;
}
[2]
这一部分代码通过自定义一个Instrumentation将此类hook进ActivityThread中从而达到跟踪目的,同时下面代码的参与是将mH类改变,mH类是AMS操作完成之后通知mH对当前应用进程进行改变的类,比如说Activity的实例产生就要通过改mH进行执行。
//ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
public static void setHandlerCallback(Context base, Handler.Callback callback) {
try {
Object activityThread = getActivityThread(base);
//activityThread对象调用getHandler得到mH对象引用final H mH = new H();
Handler mainHandler = (Handler) ReflectUtil.invoke(activityThread.getClass(), activityThread, "getHandler", (Object[])null);
//将Handler中的mCallback进行替换,替换的逻辑处理在VAInstrumentation中
ReflectUtil.setField(Handler.class, mainHandler, "mCallback", callback);
} catch (Exception e) {
e.printStackTrace();
}
}
我们此时要看一看Handler的mCallback起到什么作用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//一般情况下要执行到mCallback的handleMessage方法完了再执行handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
也就是对应:
获取下启动的Activity,然后判断如果是插件的,则对应下主题
@Override
public boolean handleMessage(Message msg) {
//当消息是LAUNCH_ACTIVITY的时候机型一些操作
if (msg.what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg.obj;//获取ActivityClientRecord
try {
Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent");//得到启动意图
intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());//得到对应类加载器
//得到ActivityInfo
ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");
//如果从插件启动则进行下面更换主题
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
activityInfo.theme = theme;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
当真正执行LAUNCH_ACTIVITY的时候是这样的
H.java
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.loadedApk = getLoadedApkNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
hookSystemServices()
这里是对IActivityManager进行hook,替换的对象是ActivityManagerProxy(插件内的)
private void hookSystemServices() {
try {
Singleton defaultSingleton;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//得到ActivityManager中的IActivityManagerSingleton对象
defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton");
} else {
defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
}
//使用动态代理将动态代理产生的代理对象赋值给activityManagerProxy
IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());
// Hook IActivityManager from ActivityManagerNative
ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;
}
} catch (Exception e) {
e.printStackTrace();
}
}
替换的原理是:对ActivityManager进行hook,IActivityManagerSingleton.get()得到的就是IActivityManager的对象然后使用动态代理对其中的方法进行拦截
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
上面这一部分主要是将当前进程通过startActivity,startService等等与AMS进行通信的渠道进行拦截,所以后面的startService等这些操作都需要经过ActivityManagerProxy(插件框架中)进行操作。
下面进入init方法,没啥操作
public void init() {
mComponentsHandler = new ComponentsHandler(this);
RunUtil.getThreadPool().execute(new Runnable() {
@Override
public void run() {
doInWorkThread();
}
});
}
private void doInWorkThread() {
}
当在MainActivity中执行
this.loadPlugin(this);
private void loadPlugin(Context base) {
PluginManager pluginManager = PluginManager.getInstance(base);
File apk = new File(Environment.getExternalStorageDirectory(), "Test.apk");
if (apk.exists()) {
try {
pluginManager.loadPlugin(apk);
Log.i(TAG, "Loaded plugin from apk: " + apk);
} catch (Exception e) {
e.printStackTrace();
}
} else {
...
}
}
通过pluginManager.loadPlugin(apk);进行装载插件
public void loadPlugin(File apk) throws Exception {
//如果apk存在的话,创建加载插件类
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
synchronized (mCallbacks) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onAddedLoadedPlugin(plugin);
}
}
plugin.invokeApplication();
} else {
throw new RuntimeException("Can't load plugin which is invalid: " + apk.getAbsolutePath());
}
}
进行创建
LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();//得到apk的路径
//将APK进行解析并执行collectCertificates进行证书操作
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;//meta-data对应的操作
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
this.mPackageInfo.packageName = this.mPackage.packageName;
if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {
throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
}
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
this.mPackageManager = new PluginPackageManager();
this.mPluginContext = new PluginContext(this);
this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE);
this.mResources = createResources(context, apk);//生成了新的Resources对象,这个要单独分析
//创建类加载器,也要单独分析
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);//赋值native的lib库
// Cache instrumentations
Map instrumentations = new HashMap();
for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
}
this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);
this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);
// Cache activities
Map activityInfos = new HashMap();
for (PackageParser.Activity activity : this.mPackage.activities) {
activityInfos.put(activity.getComponentName(), activity.info);
}
this.mActivityInfos = Collections.unmodifiableMap(activityInfos);
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
// Cache services
Map serviceInfos = new HashMap();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info);
}
this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
// Cache providers
Map providers = new HashMap();
Map providerInfos = new HashMap();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info);
}
this.mProviders = Collections.unmodifiableMap(providers);
this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// Register broadcast receivers dynamically
Map receivers = new HashMap();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii);
}
}
this.mReceiverInfos = Collections.unmodifiableMap(receivers);
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
}
上面这个构造很重要,很重要很重要,这个负责解析apk里面内容我们一步步分析
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
public static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws PackageParser.PackageParserException {
if (Build.VERSION.SDK_INT >= 24) {
if (Build.VERSION.PREVIEW_SDK_INT == 0) {
return PackageParserV24.parsePackage(context, apk, flags);
} else {
return PackageParserPPreview.parsePackage(context, apk, flags);
}
} else if (Build.VERSION.SDK_INT >= 21) {
return PackageParserLollipop.parsePackage(context, apk, flags);
} else {
return PackageParserLegacy.parsePackage(context, apk, flags);
}
}
主要进去一个,就知道原理
static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException {
PackageParser parser = new PackageParser();
PackageParser.Package pkg = parser.parsePackage(apk, flags);
ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates",
new Class[]{PackageParser.Package.class, int.class}, pkg, flags);
return pkg;
}
当你点击进去PackageParser的构造你会发现抛出一个异常,其实这里只是使用一个假的PackageParser,只是包名和类名与系统中的一样而已,所以当apk运行在系统中时候由于类的加载机制是双亲委托模型,优先使用父节点的加载器,所以优先加载系统中的PackageParser。这里调用parsePackage主要目的是解析AndroidManifest.xml,最后通过collectCertificates方法获取应用的签名信息,最后将解析完成的PackageParser.Package返回
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;//meta-data
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
this.mPackageInfo.packageName = this.mPackage.packageName;
//此插件一定没加载过才可以
if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {
throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
}
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
复制各种标签的信息,有meta-data,版本号,版本名称等等
this.mPackageManager = new PluginPackageManager();
this.mPluginContext = new PluginContext(this);
this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE);
this.mResources = createResources(context, apk);
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);
@WorkerThread
private static Resources createResources(Context context, File apk) {
if (Constants.COMBINE_RESOURCES) {
//创建Resources对象
Resources resources = ResourcesManager.createResources(context, apk.getAbsolutePath());
ResourcesManager.hookResources(context, resources);
return resources;
} else {
Resources hostResources = context.getResources();
AssetManager assetManager = createAssetManager(context, apk);
return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
}
public static synchronized Resources createResources(Context hostContext, String apk) {
Resources hostResources = hostContext.getResources();//得到应用原来的Resources
Resources newResources = null;
AssetManager assetManager;
try {
//得到AssetManager
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
assetManager = AssetManager.class.newInstance();
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", hostContext.getApplicationInfo().sourceDir);
} else {
assetManager = hostResources.getAssets();
}
//将当前apk执行AssetManager的addAssetPath添加到搜索资源的路径中
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", apk);
List pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins();
for (LoadedPlugin plugin : pluginList) {
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", plugin.getLocation());
}
//创建一个新的Resources
if (isMiUi(hostResources)) {
newResources = MiUiResourcesCompat.createResources(hostResources, assetManager);
} else if (isVivo(hostResources)) {
newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager);
} else if (isNubia(hostResources)) {
newResources = NubiaResourcesCompat.createResources(hostResources, assetManager);
} else if (isNotRawResources(hostResources)) {
newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager);
} else {
// is raw android resources
newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
// lastly, sync all LoadedPlugin to newResources
for (LoadedPlugin plugin : pluginList) {
plugin.updateResources(newResources);
}
} catch (Exception e) {
e.printStackTrace();
}
return newResources;
}
要想获得资源文件必须得到一个Resource对象,想要获得插件的资源文件,必须得到一个插件的Resource对象,在android.content.res.AssetManager.java中包含一个私有方法addAssetPath。只需要将apk的路径作为参数传入,就可以获得对应的AssetsManager对象,从而创建一个Resources对象,然后就可以从Resource对象中访问apk中的资源了。
既然现在可以加载插件中资源的Resource对象得到了,下一步就是需要将Resource对象hook进去。看代码
public static void hookResources(Context base, Resources resources) {
try {
ReflectUtil.setField(base.getClass(), base, "mResources", resources);
Object loadedApk = ReflectUtil.getPackageInfo(base);
ReflectUtil.setField(loadedApk.getClass(), loadedApk, "mResources", resources);
Object activityThread = ReflectUtil.getActivityThread(base);
Object resManager = ReflectUtil.getField(activityThread.getClass(), activityThread, "mResourcesManager");
if (Build.VERSION.SDK_INT < 24) {
Map
代码可以看出来需要hook的有两个地方一个是Context中的mResources(定义在ContextImpl中的private @NonNull Resources mResources;)一个是PackageInfo中的mResources,但是这里我们设置PackageInfo这个有问题,老版本中ContextImpl中是有这个的,但是8.0之后没有这个属性。取而代之的是:
final @NonNull LoadedApk mLoadedApk;
所以这两句代码应该加以区分版本
Object loadedApk = ReflectUtil.getPackageInfo(base);
ReflectUtil.setField(loadedApk.getClass(), loadedApk, "mResources", resources);
之后就是把mResourcesImpl也hook进Resources去
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
private static ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) {
//获取dex目录
File dexOutputDir = context.getDir(Constants.OPTIMIZE_DIR, Context.MODE_PRIVATE);
//得到绝对路径
String dexOutputPath = dexOutputDir.getAbsolutePath();
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
try {
DexUtil.insertDex(loader);
} catch (Exception e) {
e.printStackTrace();
}
}
return loader;
}
DexClassLoader先解释下参数:
- dexpath为jar或apk文件目录。
- optimizedDirectory为优化dex缓存目录。
- libraryPath包含native lib的目录路径。
- parent父类加载器。
不熟悉类加载器的,这里我从源码角度分析一下类DexClassLoader:
public classDexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
public BaseDexClassLoader(String dexPath, File optimizedDirectory,String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
protected ClassLoader(ClassLoader parentLoader) {
this(parentLoader, false);
}
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
if (parentLoader == null && !nullAllowed) {
throw new NullPointerException(“parentLoader == null && !nullAllowed”);
}
parent = parentLoader;//将父parent保存
}
继续观察this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
this.definingContext = definingContext;
ArrayList suppressedExceptions = new ArrayList();
this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
this.nativeLibraryDirectories = splitPaths(libraryPath, false);
this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true);
List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null, suppressedExceptions);
if (suppressedExceptions.size() > 0) {
this.dexElementsSuppressedExceptions = suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
} else {
dexElementsSuppressedExceptions = null;
}
}
主要是makeDexElements这个方法,这个方法就是得到一个装有dex文件的数组Element[],每个Element对象里面包含一个DexFile对象成员,它对应的就是dex文件
private static Element[] makeDexElements(ArrayList files, File optimizedDirectory,
ArrayList suppressedExceptions) {
ArrayList elements = new ArrayList();
for (File file : files) {
File zip = null;
DexFile dex = null;
String name = file.getName();
// 如果是一个dex文件
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ex) {
System.logE("Unable to load dex file: " + file, ex);
}
// 如果是一个apk或者jar或者zip文件
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
zip = file;
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException suppressed) {
suppressedExceptions.add(suppressed);
}
} else if (file.isDirectory()) {
elements.add(new Element(file, true, null, null));
} else {
System.logW("Unknown file type for: " + file);
}
//最后封装成Element
if ((zip != null) || (dex != null)) {
elements.add(new Element(file, false, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
}
对应的数据结构:
static class Element {
private final File file; // 它对应的就是需要加载的apk/dex/jar文件
private final boolean isDirectory; // 第一个参数file是否为一个目录,一般为false,因为我们传入的是要加载的文件
private final File zip; // 如果加载的是一个apk或者jar或者zip文件,该对象对应的就是该apk或者jar或者zip文件
private final DexFile dexFile; // 它是得到的dex文件
......
}
目前我们也只能是说将dex封装到Element并有个数组dexElements存起来,但是必须知道类加载的使用才能知道这些存起来有什么,所以我们继续看类加载的使用
public Class> loadClass(String className) throws ClassNotFoundException {
return loadClass(className, false);
}
protected Class> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class> clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
List suppressedExceptions = new ArrayList();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
pathList就是前面创建的DexPathList对象,从上面我们知道,我们加载的dex文件都存放在它的exElements成员变量上面,dexElements就是Element[]数组,所以可以看到BaseDexClassLoader的findClass方法调用的是pathList的findClass方法
BaseDexClassLoader
public Class findClass(String name, List suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
可以看到它就是遍历dexElements数组,从每个Element对象中拿到DexFile类型的dex文件,然后就是从dex去加载所需要的class文件,直到找到为止。
一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。
所以那些插件化的类如何加载到内存中呢?
基本原理:
1、除了第一个dex文件(即正常apk包唯一包含的Dex文件),其它dex文件都以资源的方式放在安装包中。所以我们需要将其他dex文件并在Application的onCreate回调中注入到系统的ClassLoader。并且对于那些在注入之前已经引用到的类(以及它们所在的jar),必须放入第一个Dex文件中。
2、PathClassLoader作为默认的类加载器,在打开应用程序的时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex),也就是第一个dex文件是PathClassLoader自动加载的。所以,我们需要做的就是将其他的dex文件注入到这个PathClassLoader中去。
3、因为PathClassLoader和DexClassLoader的原理基本一致,从前面的分析来看,我们知道PathClassLoader里面的dex文件是放在一个Element数组里面,可以包含多个dex文件,每个dex文件是一个Element,所以我们只需要将其他的dex文件放到这个数组中去就可以了。
实现:
1、通过反射获取PathClassLoader中的DexPathList中的Element数组(已加载了第一个dex包,由系统加载)
2、通过反射获取DexClassLoader中的DexPathList中的Element数组(将第二个dex包加载进去)
3、将两个Element数组合并之后,再将其赋值给PathClassLoader的Element数组
我们回归滴滴的插件中如何实现的:
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
try {
DexUtil.insertDex(loader);
} catch (Exception e) {
e.printStackTrace();
}
}
所以实现方法在于DexUtil.insertDex(loader);
public static void insertDex(DexClassLoader dexClassLoader) throws Exception {
Object baseDexElements = getDexElements(getPathList(getPathClassLoader()));
Object newDexElements = getDexElements(getPathList(dexClassLoader));
Object allDexElements = combineArray(baseDexElements, newDexElements);
Object pathList = getPathList(getPathClassLoader());
ReflectUtil.setField(pathList.getClass(), pathList, "dexElements", allDexElements);
insertNativeLibrary(dexClassLoader);
}
将主dex中的类加载器中的pathList得到,然后得到其中的dexElements
```java
private static Object getPathList(Object baseDexClassLoader) throws Exception {
return ReflectUtil.getField(Class.forName("dalvik.system.BaseDexClassLoader"), baseDexClassLoader, "pathList");
}
private static Object getDexElements(Object pathList) throws Exception {
return ReflectUtil.getField(pathList.getClass(), pathList, "dexElements");
}
"se-preview-section-delimiter">
Object newDexElements = getDexElements(getPathList(dexClassLoader));
"se-preview-section-delimiter">
然后拿到新的类加载器中的dex,因为这个里面装了插件apk的dex。下一步将两个进行混合
Object allDexElements = combineArray(baseDexElements, newDexElements);
private static Object combineArray(Object firstArray, Object secondArray) {
Class> localClass = firstArray.getClass().getComponentType();
int firstArrayLength = Array.getLength(firstArray);
int allLength = firstArrayLength + Array.getLength(secondArray);
Object result = Array.newInstance(localClass, allLength);
for (int k = 0; k < allLength; ++k) {
if (k < firstArrayLength) {
Array.set(result, k, Array.get(firstArray, k));
} else {
Array.set(result, k, Array.get(secondArray, k - firstArrayLength));
}
}
return result;
}
"se-preview-section-delimiter">
然后再将老的pathList反射出来,将新融合的dex数组塞进去这样老的里面就融合了新的dex文件,下一步代码是对native的lib包进行融合,具体看插件代码
将清单文件中的所有instrumentations,activity,services,providers,broadcast都添加到列表中。
到了这里LoadedPlugin类中存在的信息真多啊,把apk的资源,清单文件里面的资源都整合了。
此时apk里面的信息已经都获取到了并且添加到资源与类加载器中了,继续看
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
synchronized (mCallbacks) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onAddedLoadedPlugin(plugin);
}
}
// try to invoke plugin's application
plugin.invokeApplication();
"se-preview-section-delimiter">
public void invokeApplication() {
if (mApplication != null) {
return;
}
// make sure application's callback is run on ui thread.
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
mApplication = makeApplication(false, mPluginManager.getInstrumentation());
}
}, true);
}
private Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (null != this.mApplication) {
return this.mApplication;
}
String appClass = this.mPackage.applicationInfo.className;
if (forceDefaultAppClass || null == appClass) {
appClass = "android.app.Application";
}
try {
this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());
instrumentation.callApplicationOnCreate(this.mApplication);
return this.mApplication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
执行插件的Application.OnCreate
到此为止我们插件的初始化部分就完成了,下一篇我们继续跟踪启动插件apk内的activity
然后再将老的pathList反射出来,将新融合的dex数组塞进去这样老的里面就融合了新的dex文件,下一步代码是对native的lib包进行融合,具体看插件代码
---
第五步
----
将清单文件中的所有instrumentations,activity,services,providers,broadcast都添加到列表中。
到了这里LoadedPlugin类中存在的信息真多啊,把apk的资源,清单文件里面的资源都整合了。
此时apk里面的信息已经都获取到了并且添加到资源与类加载器中了,继续看
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
synchronized (mCallbacks) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onAddedLoadedPlugin(plugin);
}
}
plugin.invokeApplication();
```java
public void invokeApplication() {
if (mApplication != null) {
return;
}
// make sure application's callback is run on ui thread.
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
mApplication = makeApplication(false, mPluginManager.getInstrumentation());
}
}, true);
}
private Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (null != this.mApplication) {
return this.mApplication;
}
String appClass = this.mPackage.applicationInfo.className;
if (forceDefaultAppClass || null == appClass) {
appClass = "android.app.Application";
}
try {
this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());
instrumentation.callApplicationOnCreate(this.mApplication);
return this.mApplication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
执行插件的Application.OnCreate
到此为止我们插件的初始化部分就完成了,下一篇我们继续跟踪启动插件apk内的activity