插件化开发详解
Posted 蜗牛攀爬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插件化开发详解相关的知识,希望对你有一定的参考价值。
1:替换DexElements流程:
插件化原理:https://www.cnblogs.com/wnpp/p/16053088.html
插件生成apk,宿主通过反射机制和类加载器(传入插件apk),获取到插件的dexElements,并将dexElements合并到宿主的类加载器的dexElements,
这样插件所有的class都位于宿主的类加载器里面,达到宿主可以启动插件的目的。
2:启动插件普通类代码流程:
1)Plugin module:
public class Test public int add(int a, int b) return a + b; ;
编译生成plugin.apk,放到sdk目录下
2)Host module:
public class LoadUtil private static final String apkpath = "/sdcard/plugin.apk"; public static void loadClass(Context context) //反射流程 //1)获取class //2)获取class中我们需要的那个属性Filed //3)Field.get(实例化对象),得到属性对应的那个实例 //4)通过以上方法分别获取host的dexElements对象和plugin的dexElements //两层:classLoader得到pathList实例,pathList实例得到DexPathList实例 //BaseDexClassLoader->pathList->DexPathList try // 获取DexPathList的class Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList"); //获取DexPathList的dexElements属性 Field dexElementField = dexPathListClass.getDeclaredField("dexElements"); //将dexElements属性设置为public dexElementField.setAccessible(true); //获取BaseDexClassLoader的class Class<?> classLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader"); //获取pathList属性 Field pathListField = classLoaderClass.getDeclaredField("pathList"); pathListField.setAccessible(true); //获取数组的类加载器,get(实例化对象)可以获取到对象的值 //1.获取宿主的类加载器 ClassLoader pathClassLoader = context.getClassLoader(); //通过BaseClassLoader的实例化对象获取到pathList的实例化对象 Object hostPathList = pathListField.get(pathClassLoader); //通过pathList的实例得到elements的对象 Object[] hostDexElements = (Object[]) dexElementField.get(hostPathList); //2.插件 ClassLoader pluginClassLoader = new DexClassLoader(apkpath, context.getCacheDir().getAbsolutePath(), null, pathClassLoader); //通过BaseClassLoader的实例化对象获取到pathList的实例化对象 Object pluginPathList = pathListField.get(pluginClassLoader); //通过pathList的实例得到elements的对象 Object[] pluginDexElements = (Object[]) dexElementField.get(pluginPathList); //合并 //new Elements[] Object[] newElements = (Object[]) Array.newInstance(hostDexElements.getClass().getComponentType(), hostDexElements.length+pluginDexElements.length); System.arraycopy(hostDexElements, 0, newElements, 0, hostDexElements.length); System.arraycopy(pluginDexElements, 0, newElements, hostDexElements.length, pluginDexElements.length); //赋值到宿主的dexElements //hostDexElements = newElemnts dexElementField.set(hostPathList, newElements); catch (ClassNotFoundException | IllegalAccessException e) e.printStackTrace(); catch (NoSuchFieldException e) e.printStackTrace();
Application启动:
public class MyApplication extends Application @Override public void onCreate() super.onCreate(); LoadUtil.loadClass(this);
启动插件:
try Class<?> clazz = Class.forName("com.example.hotfixplugin.Test"); Method add = clazz.getMethod("add"); Object obj = add.invoke(clazz.newInstance(), 1, 2); Log.d("test", obj.toString()); catch (ClassNotFoundException | NoSuchMethodException | java.lang.InstantiationException e) e.printStackTrace(); catch (InvocationTargetException e) e.printStackTrace(); catch (IllegalAccessException e) e.printStackTrace();
在创建一个新的应用进程之后,系统首先会启动ActivityThread,ActivityThread是应用进程的主线程,在ActivityThread创建的时候会创建一个ApplicationThread的对象。这个ApplicationThread实现了一个Binder的服务端。新的进程创建完成之后通知AMS服务的之后同时把自己进程的ApplicationThread的代理端送给AMS服务。AMS服务中保存了所有应用进程的ApplicationThread的代理对象。所以AMS要想给应用进程发送消息,只需要得到目标应景进程的ApplicationThread的代理端对象即可。
滴滴插件化方案:https://github.com/didi/VirtualAPK
Activity:假设要启动插件中的Activity1,我们伪装一个Activity2骗过系统,预先注册在AndroidManifest.xml中,占个坑;
1)创建一个VasIinstruentation,通过反射机制和代理模式,替换掉系统中的Instrumentation,所有经过Instrumentation的操作都会到VasInstumentaion替代掉。
2)这时startActivity是在VasInstrumentation中执行,startActivity实际会调用到AMS中执行,因为AMS会对要启动的Activity1是否注册过进行校验。我们先保存Activity1的信息,然后告诉AMS我们要启动的是startActivity2。AMS看到启动的是Activity2,就通过校验。
3)AMS的作用:
a:对Activity的注册进行校验
b:栈的调度
c:AMS作为服务端,进行生命周期的管理,Client端的ActivityThread负责响应各个生命周期
4)AMS启动Activity2之后,根据上面流程图可知,最终会回到应用的mInstrumentation.newActivity(),newActivity通过类加载器生成实际上的Activity对象,我们的VasInstrumentation就可以对该方法进行重写,把原来实际要启动的Activity1的信息重新提取出来,替换掉当前的Activity2,生成Activity2对象,就完成了正常的Activiyt1启动。
4:查找Hook点的原则:
1)尽量静态变量或者单例对象:有利于反射和动态代理,反射的时候,如果不是静态的,就需要往前面找,直到可以得到一个类的对象为止。
2)尽量Hook public的对象和方法:谷歌提供给外面使用的,一般不会怎么修改。
以上是关于插件化开发详解的主要内容,如果未能解决你的问题,请参考以下文章
如何将Android程序做成插件化的形式?详解插件化实现原理