Android动态加载之热修复与插件化

来源:互联网 发布:淘宝品牌男装授权 编辑:程序博客网 时间:2024/05/22 12:31

Android动态加载之热修复与插件化

  • Android热修复和插件化使用背景
  • 原理
  • 开源框架对比
  • AndFix热修复框架使用
  • Small插件化框架的使用
  • 总结

技术背景

  在android开发中,正式的项目会遇到两个难以避免的问题。第一个问题就是:如果项目在发布以后,某个功能出现了bug了怎么办?而这个bug并不是在程序框架上的(你想,你开发的软件上线了,一登陆就出bug,那boss只有叫你走人了),所以这种bug只是一个业务逻辑上的,但是纠正这个bug去让用户下载更新包可能会流失那些非铁杆用户,所以必须用一种在线纠正bug的方法,神不知,鬼不觉就纠正bug了,提高用户体验。第二个问题:当我们的项目已经发布了,但是我们想在一个功能模块上进行升级,让这个功能模块更加强大,这怎么实现?其实第二个也不算问题,只是一个技术的实现而已。下面我们用动态加载技术来一一解决。

原理

基本原理
    我们都知道,android业务逻辑层是java语言开发的。在java中,当我们写了一个xx.java的类,通过java Compiler编译就变成了xx.class文件(字节码文件),然后java虚拟机将class文件的文件信息放入虚拟机的方法区(方法区存储已经被加载的class文件的类信息,静态变量,常量),之后JVM会创建一个引导类加载器(BootStrap ClassLoader)实例,加载基本的系统的一些类,如:java.lang.String,加载完成后创建一个单例的Launcher,其中包含了两个关键的类加载器:Extension ClassLoader和Application ClassLoader,Extension ClassLoader是加载JAVA_HOME\lib\ext下面的一些类库,Application ClassLoader就是完成加载应用层的类,在加载类的时候是采用双亲委派模型(该模型中,一个类加载器只有一个父加载器,当我们加载类的时候,Application ClassLoader会首先判断有没有父加载器Extension ClassLoader,有则交给父加载器,如果没有就去找BootStrap ClassLoader,看引导类加载器是否加载过该类,父加载器没有加载成功,又会去找引导类加载器,查询该类是否已经加载,如果父加载器没有加载成功而且引导类加载器又找不到,那就只有Application ClassLoader自己加载了,如果还是加载不成功,则抛出异常ClassNotFoundException),在类加载器加载成功之后会在Heap区存放该类的对象。
热修复之dex替换
    在android中,针对Dalvik虚拟机,Android SDK提供了DexClassLoader来加载类,因为在Dalvik虚拟机Runtime环境下加载的并不是class文件,而是一种经过优化了的dex文件(在android sdk的builder-tools目录下有一个aapt工具可以将class文件优化成dex文件),然后由DexClassLoader来加载该文件中的类。但是讲了这么多,给修复bug没半毛钱关系啊(当然大神看到这里已经知道怎么做了)!试想,如果一个已经发布了的app,我们在它没有运行到有bug之前,将有bug的那个类替换掉不就解决bug了吗?那么有bug的那个类就不会被加载到我们的运行时刻了。但是我们任然要弄明白如何替换替换有错误的那个类的,其实它是通过替换dex来替换的。在AndroidSDK中,有一个BaseClassLoader类(DexClassCloader继承该类),里面有一个DexPathList对象(将其看成集合对象),该对象有序地装有dex文件,所谓有序就是类加载器首先会加载该集合前面的dex中的类,所以我们只需要将dex文件插入到有错误的dex文件之前,就可以替换它了。
如果通过该方式进行热修复,先要将项目进行dex拆分,比如将做全局处理的Application类拆分成calss.dex(程序主框架拆分成一个dex),分支业务逻辑拆分成class2.dex等等。对于技术不强的小牛来说还是有一定要求的。
热修复之native层替换
   阿里开源框架Andfix使用的是另一种方法,但是有一定局限性,因为该方法是通过二进制工具apkpatch将新生成的apk与原来的apk对比生成差异包xx.patch,然后在程序运行的时候通过反射和注解,将已经加载到内存的xx.apatch文件中的某个方法与要修改的方法在native层进行替换(该框架是替换方法,不是替换dex文件)。
插件化
    要实现插件化主要需要解决两个问题,一是如何调用插件的某个Activity(刁钻的我们甚至想直接通过startActivity(intent)直接完成),二是如何使用插件里面的res。当然插件化技术针对这两个问题又可以细分成几种,有通过接口和代理Activity的dynamic-load-apk开源框架,在宿主apk中建立一个ProxyActivity,在ProxyActivity的生命周期中体调用插件Activity相应的生命周期 ,如果要使用资源,则新建一个AssetManager实例来访问资源;也有暴力的Small开源框架,通过反射替换ActivityThread里的mInstrumentation,在Instrumentation的newActivty实现里面实例化了插件Activity(这里不懂得可以去了解Activity的实例化过程),在资源处理方面,Small是修改插件资源索引二进制文件,这样只需一个AssetManager就可以用插件中的资源,解决了资源id冲突。相比之下,dynamic-load-apk实现起来更佳繁琐,支持上没有Small强大,Small比较完美,但是难度较大。

开源框架的对比

\ dynamic-load-apk Small 加载非独立插件 N Y 加载.so文件 N Y Activity生命周期 Y Y Service动态注册 N N 资源分包共享 N Y 支持AppCompat N Y 支持本地网页组件 N Y 支持联调插件 N Y

AndFix的使用

如果在android sdudio中使用,先添加赖:

compile 'com.alipay.euler:andfix:0.4.0@aar'

在Application类中进行初始化作操

public class AndFixApplication extends Application {    public static PatchManager mPatchManager;    @Override    public void onCreate() {        super.onCreate();        //初始化管理类        mPatchManager = new  PatchManager(this);        //初始化版本        mPatchManager.init("1.0");        //加载已经被加载到PatchManager中的patch,是从缓存中获取,当缓存删除之后就不能加载,要么从本地外部内存加载,如果外部也被删除,就从服务器下载        /**         * apllication类工作顺序:         * 1.需要修复新版本吗?网上下载apatch文件:内存加载         * 2.内存被清空?从apk系统文件中加载:内存加载         * 3.系统文件(外存)被删除?网上下载:外存加载         * 该工作也可以交给网络广播         */       mPatchManager.loadPatch();  }}

修复前MainActivity中的关键代码

public void update( ) {     String filePath = Environment.getExternalStorageDirectory( ).getAbsolutePath( )+APATH_PATH;     try {           AndFixApplication.mPatchManager.addPatch(filePath);     }catch(Exception e){           }

该方法我们可以设置在onCreate()中点击按钮之后执行,APATH_PATH是手机存储差异包xxx.apatch的位置,AndFixApplication是我们自己定义的Application类,记得在AndroidManifest.xml中使用。

我们再写个showToast()方法查看效果

public void showToast(){     Toast.makeText(MainActivity.this,"fixBefore",Toast.LENGTH_SHROT).show();}

showToast()方法也设置成点击一个按钮之后执行该代码,在修复之前肯定弹出fixBefore。完成到这一步,我们就打包程序,生成fixTest1.apk,然后我们把showToast()方法中的”fixBefore”改成”fixAfter”,重新打包生成fixTest2.apk。到这一步就要用到一种生成差异包的工具——apkpatch。
在apkpatch的文件中打开命令行(shift+鼠标右键),输入:apkpatch.bat -f 新apk -t 有bug的旧apk -o 生成文件的存放地址 -k 打包用的xxx.jks签名文件  -p 签名文件密码 -a 签名文件别名 -e 别名密码。输入完成之后会在文件的存放地址生成3个文件,一个是smali文件,一个是diff.dex文件,最后一个就是我们需要的文件了。将该文件改名为fix.apatch,拷贝到手机根目录,该文件的地址就是我们之前update()方法中的filePath。到此就可以运行了。

Small框架的使用

Small的使用直接上[官方指导],不过当中一定要注意assets文件一定要放在宿主apk的main文件夹下。

总结

    动态加载技术有各种方法,而且各有特色,我们不必要一一使用,要针对自己的项目。其实动态加载技术到目前都不是非常的成熟,有些大牛或者高级架构师还称这是一个踏坑之路,谷歌也不建议使用这相关的技术,甚至不久出来的React Native也可以实现动态更新了,而且还是跨平台的,这简直不给活路啊!不过学习一种思想也是一种好事,未来是又说得清呢!


1 0
原创粉丝点击