插件化记录

来源:互联网 发布:淘宝蜜琪美妆是假货吗 编辑:程序博客网 时间: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

  1. 注意此处要首先声明一个Android library的module 不是application
  2. 正常写代码 在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。

  1. 动态加载未安装的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()一样都是同一目录即安装目录,之后操作一样。

0 0
原创粉丝点击