插件化记录
来源:互联网 发布:淘宝蜜琪美妆是假货吗 编辑:程序博客网 时间:2024/06/02 05:11
插件化记录
一,这里先说明 动态修复和插件化的关系
1. 动态修复和插件化共同技术点 都用到了 动态加载
2. 动态修复最核心的技术是dex分包和动态注入字节码文件
3. 插件化则相对简单即动态加载
二 ,插件化
1. 主要分为jar包插件化
2. apk插件化
3. apk插件化又分为安装插件化和非安装插件化
4. zip包
动态加载jar
1,这里用的androidStuido开发既然要插件化 ,就要先准备好jar包 这里并不是java普通的jar,因为java虚拟机会把.java文件直接变成class文件,然后在优化成.jar,而android虚拟机Dalvik则需要进一步优化会把.class优化成一个或者几个dex文件,所以这里的jar就要转成dex格式的jar.
2,jar在实际开发中一般存到服务器,客户端只需要请求下载就行,然后动态加载。
3,动态加载 在初始化的地方进行动态加载,一般在application的onCreate中
那么我们先打一个jar
- 注意此处要首先声明一个Android library的module 不是application
- 正常写代码 在module的build.gradle中配置一个task,gradle的执行都是通过任务执行了,它是用groovy脚本语言编写的 比如apply plugin: ‘xxxx’其实就是一个函数的调用(这里不在多说有兴趣的同学可以看看groovy语法)。
比如 创建一个jar的任务声明
task makeJar(type: Copy) { 此处是声明一个任务类型就是继承Copy类
delete ‘build/libs/mysdk.jar’//创建之前删除以前的
from(‘build/intermediates/bundles/release/’)//把哪些class文件打包
into(‘build/libs/’)//生成之后放在那里
include(‘classes.jar’)//包含之前的classes.jar
rename (‘classes.jar’, ‘mysdk.jar’)//改名
}
makeJar.dependsOn(build) //必须是makeJar和任务名字一致
gradlew makeJar //在androidStuido终端执行 这个任务
其实也不用我们创建只要build过就会产生一个classess.jar文件 跟上述一样的jar就可以用。
3. 把打好的jar进行dex转格式
1. 首先把jar你的androidSdk的build-tools并且选择一个相应的版本目录下,比如我的在F:\Android\sdk\build-tools\23.0.3,可以看到dx.bat 然后打开cmd命令切换到该目录下执行dx命令dx –dex –output=plugin.jar mysdk.jar 前面固定命令格式,后面时可选参数output输出到 哪里, 我的就是输出到当前目录名字是plugin.jar 后面是你要转格式的jar路径,因为这里在dx.bat的目录下就直接写了。
前面已经说过一般方到服务器 然后下载之后动态加载
动态加载
讲动态加载之前要先说明java类的加载都需要类加载器,而在android中类即是BaseDexClassLoader 它是一个类加载器, 而该类有两个子类即DexClassLoader 和PathClassLoader。
DexClassLoader 可以加载ar和apk中的classes.dex文件,而PathClassLoader只能加载安装过后的akp中的dex文件。
所以这里我们就要用DexClassLoader类进行加载。
DexClassLoader loader = new DexClassLoader(file.getAbsolutePath(), "/data/data/" + packageName, null, loadPackageParam.getClass().getClassLoader());
这个构造方法 有四个参数
第一个参数就是jar存储路径,这里是sdcard下的一个目录,
第二个参数 就是存放从jar和apk中提取到的classes.dex这里要说明一下这个路径需要一个程序私有的,可写的文件目录去存放优化后的classes文件。通过Contexct.getDir(String, int)来获得该目录,即data/data/包名/下,因为此处我们使用的了xposed框架,所以当前目录就不能写具体某个应用的目录,只能是拦截哪个应用就用哪个目录加载。
第三个参数如果用到一些so库就填写它的目录,此处没有用到null,
第四个参数就是ClassLoader,此处用的是Xposed里面的classLoader,一般没用到直接获取系统的就行 content.getClassLoader()
这样就加载完后.
接下来就是利用DexClassLoader对象获得你想要的Class ,再反射等
Class cls =loader.loadClass(“com.test.interceptor.InterceptorImpl”);
动态加载apk
1 和动态加载jar相比动态加载apk,apk中可以有资源文件等,所以当需要资源文件时,就需要动态加载apk。
动态加载未安装的apk
1.从服务器下载到本地,比如到getCacheDir();
2.动态加载 这里是未安装的所以用的还是DexClassLoader该类加载
3.调用该apk中的 比如xml java文件,图片文件
这里重点讲第三步
平时我们this.getResources().getDrawable();获得资源文件,注意此处的context是本应用的上下文,在本应用下获得的resources肯定在本应用下查找,而这里我们要用到另一个apk的资源文件,所以就用该获用另一个应用的context获取Resources。因为此处调用的是别的apk中的资源文件,所以这里就要用另一个resources 即继承系统的Resources具体代码
public static PluginResources newInstance(File file,Resources res) { AssetManager assetManager = getAssetManager(file); PluginResources pluginResources=new PluginResources(assetManager,res.getDisplayMetrics(),res.getConfiguration()); return pluginResources; } /** * 别人的assetManager 最主要的就是别人的安装路径及资源文件路径 * 参数 file 即apk文件的存放地址 * @return */ public static AssetManager getAssetManager(File file){ //获得AssetManager的class对象,只能反射调用 Class assetManagerClass = Class.forName("android.content.res.AssetManager"); //获得AssetManager 对象 AssetManager assetManager = (AssetManager) assetManagerClass.newInstance(); //调用addAssetPath()方法就表示此AssetManager 就是一个加载该路径下的AssetManager Method addAssetPathMethod = assetManagerClass.getDeclaredMethod("addAssetPath",String.class); addAssetPathMethod.invoke(assetManager,file.getAbsoluteFile()); return assetManager; }
通过此类的newInstance(Flie flie,Resuoce res)就可以获得file文件下的apk的资源文件了.
接下来动态加载通过DexClassLoader
然后通过反射获得某个资源的名字获得具体id,通过id获得资源
比如这里要获得一个anim文件下一个xml的文件名即name
例子代码
PluginResources pluginResources = PluginResources.newInstance(file, getResources());//获得该apk的资源对象 DexClassLoader loader = new DexClassLoader(file.getAbsolutePath(),getDir("/dex",0).getAbsolutePath(),null,getClassLoader());//动态加载 Class<?> animClass = loader.loadClass("apk的包名.R$anim");//反射获得R中内部类anim中的class文件 Field xxxFiled = animClass.getDeclaredField("xxx"); Object id = xxxFiled.get(null);//获得具体属性的id pluginResources.getDrawable(id);//获得具体动画图片
到此 动态加载未安装apk结束。
注意此处安装未加载的apk其实也可以通过声明一个activity重写一些方法通过该activity打开两一个apk的页面,必须把另一个apk进入页面时一个splashFragment就可以直接把该fragment添加到该activity中但,操作比较麻烦,需要控制其生命周期等。
动态加载安装apk
其他步骤一样 最主要的就是apk已经安装了,所以我们就不必重写resouces类了,直接就可以获取
Context apkContext = createPackageContext(“apk的包名”, CONTEXT_IGNORE_SECURITY | CONTEXT_INCLUDE_CODE);//参数就是包名,另一个忽略安全检查和允许调用代码。
动态加载就是用PathClassLoader使用更简单
PathClassLoader pathClass=new PathClassLoader(apkContext.getPackageResourcePath(),getClassLoader());
上面第一个参数传apkContext.getPackageCodePath()一样都是同一目录即安装目录,之后操作一样。
- 插件化记录
- 插件记录
- 插件开发积累记录
- Openfire 插件开发记录
- log4net 日志记录插件
- 插件30:引用记录
- openfire插件编译记录
- js、jquery插件记录
- eclipse插件记录
- vim插件使用记录
- sublime 插件 收集记录
- vim插件使用记录
- OCX插件开发记录
- My97DatePicker 插件学习记录
- jquery 插件网站记录
- Sublime插件记录
- NPAPI插件开发详细记录:脚本化接口
- Android 插件化 RePlugin 入坑记录一
- nested exception is java.sql.SQLException: Data truncated for column 'PassWord' at row 72
- 【WebService】7.为CXF的客户端和服务器端添加拦截器
- fnd_concurrent.wait_for_request
- Java对象和XML相互转换
- LC_ALL的错误
- 插件化记录
- bzoj1798: [Ahoi2009]Seq 维护序列seq
- Builder模式(建造者模式 创建型)
- Android驱动利用sys节点进行调试
- request.getParameter() 、 request.getInputStream()使用冲突
- 使用Redis计数器防止并发请求
- 【备战NOIP2012图论专项模拟试题】无线通讯网
- Android——使用ProgressBar实现进度条
- hdu3397Sequence operation--线段树