前言 

谈到热修复相信大家应该比较熟悉,因为它是目前比较重要的技术,平常面试中也是被问的比较多。插件化和热修复同出一门,俩者都属于动态更新,而模块化和组件化是基础。相信看完本篇的内容,对于这些模糊的概念应该会有一个比较清晰的了解。

一、模块化

二、组件化

三、插件化

四、热修复

五、总结


一、模块化

1、概念

模块(Module),Android Studio提出的概念,根据不同关注点将原项目中共享的部分或业务抽取出来形成独立module,这就类似我们最集成的第三方库的SDK。

2、思想

实际开发中,我们通常会抽取第三方库、整个项目的初始化的代码、自定义的Utils工具类、自定义View 、图片、xml这些(value目录下的各种xml文件)等到一个共有的Common模块中,其他模块在配置Gradle依赖后,就能够调用这些API。

特别注意的是style.xml文件,对于全局共用的style,我们应该把它也放在common模块中。例如我们的项目theme主题,本来是放在main组件的style里面,我们可以把它移到common中,这样其他组件调试时,作为一个单独的项目,也能和主项目有一样的主题。

 总之,你认为需要共享的资源,都应该放在common组件中。

3、使用

每一个Module都可以在自身的 build.gradle 中进行设置两种格式:application和library。 

apply plugin: 'com.android.application'//或apply plugin: 'com.android.library'

引用时,就像添加依赖GitHub库一样。

 

二、组件化

1、概念

组件化是基于模块化的可以在打包时是设置为library,开始调试运行是设置成application。目的是解耦与加快开发。组件化适用于多人合作开发的场景,隔离不需要关注的模块,大家各自分工、各守其职。简而言之,就是把一个项目分开成多个项目

(1)好处

  • 业务模块分开,解耦的同时也降低了项目的复杂度。
  • 开发调试时不需要对整个项目进行编译。
  • 多人合作时可以只关注自己的业务模块,把某一业务当成单一项目来开发。
  • 可以灵活的对业务模块进行组装和拆分。

(2)规则

  • 只有上层的组件才能依赖下层组件,不能反向依赖,否则可能会出现循环依赖的情况;
  • 同一层之间的组件不能相互依赖,这也是为了组件之间的彻底解耦;

2、使用

1、在整个项目 gradle.properties 文件中,添加代码

#是否处于debug状态isDebug = flase

 

2、在其他Module的 build.gradle 文件中,添加代码

if (isDebug.toBoolean()) {    apply plugin: 'com.android.application'} else {    apply plugin: 'com.android.library'}

3、在宿主Module的 build.gradle 文件中,添加代码

dependencies {    compile fileTree(include: ['*.jar'], dir: 'libs')    //...    if(!isDebug.toBoolean()){//不是debug,就添加依赖其他模块        compile project(':home')        compile project(':personal')        compile project(':video')    }    if(isDebug.toBoolean()){        compile project(':common')    }}

3、版本管理

每个Module的build.gradle文件中很多地方需要些写版本号,例如 targetSdkVersion、appcompat-v7、第三方库等。修改时都要同时修改多份build.gradle文件。如果把版本号可以统一管理起来,就会省时省力,又避免不同的组件使用的版本不一样,导致合并在一起时引起冲突。

整个项目根目录下的 build.gradle 文件中,添加代码

ext {    compileSdkVersion = 25    buildToolsVersion = "25.0.2"    minSdkVersion = 14    targetSdkVersion = 25    versionCode = 1    versionName = "1.0"}

每个Mudule的 build.Gradle 文件中,改写代码

android {    compileSdkVersion rootProject.ext.compileSdkVersion    buildToolsVersion rootProject.ext.buildToolsVersion    defaultConfig {        minSdkVersion rootProject.ext.minSdkVersion        targetSdkVersion rootProject.ext.targetSdkVersion        versionCode rootProject.ext.versionCode        versionName rootProject.ext.versionName    }//...}

4、模块间跳转

我们知道,通常在Gradle中依赖的库是可以直接引用的,即通过startActivity跳转。根据组件化的规则,宿主可以依赖下层组件,而组件之间不可以依赖。因此,当常规业务模块之间遇到业务需求,进行互相跳转时该怎么处理?

这里简单介绍两种方式,即路由和反射。路由的方式以用阿里的ARouter/美团的WMRouter,但是我觉得人少、项目小的公司必要用到这么强大的工具,直接反射就好。

放在common组件中的EventUtile工具类

public class EventUtil{    /**     * 页面跳转     * className  全路径类名     */    public static void open(Context context,String className){        try {            Class clazz = Class.forName(className);            Intent intent = new Intent(context,clazz);           context.startActivity(intent);        } catch (ClassNotFoundException e) {            Log.e("zhuang","未集成,无法跳转");        }    }    /**     * 页面跳转,可以传参,参数放在intent中,所以需要传入一个intent     */    public static void open(Context context,String className,Intentintent){        try {            Class clazz = Class.forName(className);           intent.setClass(context,clazz);           context.startActivity(intent);        } catch (ClassNotFoundException e) {            Log.e("zhuang","未集成,无法跳转");        }    }}

5、资源命名问题

首先,多组件集成时,特别容易出现资源命名重复的问题。可以让各个组件中使用统一前缀,比如home组件中的资源,以home_开通、video组件中以video_开头。当然,如果是嫌麻烦,我们可以在build.gradle文件中,加入如下代码:

resourcePrefix"home_"

但是这个功能其实很弱。比较xml文件报错,依然可以运行,图片文件不已home_为前缀,也不会报错。

三、插件化

也是属于模块化的一种体现。将完整的项目按业务划分不同的插件,分治法,越小的模块越容易维护。单位是apk,一个完整的项目。插件化比热修复简单,插件化只是增加新的功能或资源文件。灵活性在于加载apk,按需下载,动态更新。

实现原理

  1. 通过dexclassloader加载。
  2. 代理模式添加生命周期。
  3. hook思想跳过清单验证。

Android 使用Java的反射机制总结

Android 动态代理与Hook机制详解

总结

  • 宿主和插件分开编译
  • 动态更新插件
  • 按需下载插件
  • 缓解65535方法数限制

四、热修复

1、概述

热修复与插件化都利用classloader实现加载新功能。热修复比插件化复杂,插件化只是增加新的功能或资源文件,所以不涉及抢先加载旧类的使命。热修复为了修复bug,要将新的同名类替旧的同名bug类,要抢在加载bug类之前加载新的类。

2、流派

   热修复作为当下热门的技术,在业界内比较著名的有阿里巴巴的AndFix、Dexposed,腾讯QQ空间的超级补丁和微信的Tinker,以及大众点评nuwa和美团Robust。阿里百川推出的HotFix热修复服务就基于AndFix技术,定位于线上紧急BUG的即时修复。虽然Tinker支持修复的功能强大兼容性很好,但是不能即时生效、集成负责、补丁包大。

3、原理

(1)native修复方案

AndFix 

提供了一种运行时在Native修改Filed指针的方式,实现方法的替换,达到即时生效无需重启,对应用无性能消耗的目的。实现过程三步骤:

  • setup():对于Dalvik的即时编译机制(JIT),在运行时装载libdvm.so动态链接库,从而获取native层内部函数:dvmThreadSelf( ):查询当前的线程;dvmDecodeIndirectRef( ):根据当前线程获得ClassObject对象。
  • setFieldFlag():把 private、protected的方法和字段都改为public,这样才可被动态库看见并识别,因为动态库会忽略非public属性的字段和方法。
  • replaceMethod():该步骤是方法替换的核心。拿到新旧方法的指针,将指针指向新的替换方法来实现方法替换。

(2)Dex 分包方案

概述

DEX分包是为了解决65536方法限制,系统在应用打包APK阶段,会将有调用关系的类打包在同一个Dex文件中,并且同一个dex中的类会被打上`CLASS_ISPREVERIFIED`的标志。因为加载后的类不能卸载,必须通过重启后虚拟机进行加载才能实现修复,所以此方案不支持即时生效。

 QQ空间超级补丁

是把BUG方法修复以后放到一个patch.dex,拿到当前应用BaseDexClassloader后,通过反射获取到DexPathList属性对象pathList、再反射调用pathList的dexElements方法把patch.dex转化为Element[],两个Element[]进行合并,最后把patch.dex插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法,就可以达到修复目的。

问题 

而然,问题就是两个有调用关系的类不再同一个Dex文件中,那么就会抛“unexpected DEX problem”异常报错。解决办法,就是单独放一个AnitLazyLoad类在另外DEX中,在每一个类的构造方法中引用其他DEX中的唯一AnitLazyLoad类,避免类被打上CLASS_ISPREVERIFIED标志。

不足 

此方案通过增加dex来修复,但是修复的类到了一定数量,就需要花不少的时间加载。对手淘这种航母级应用来说,启动耗时增加2s以上是不能够接受的事。在ART模式下,如果类修改了结构,就会出现内存错乱的问题。为了解决这个问题,就必须把所有相关的调用类、父类子类等等全部加载到patch.dex中,导致补丁包异常的大,进一步增加应用启动加载的时候,耗时更加严重。

微信Tinker

微信Tinker采用的是DEX差量包,整体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,但不将patch.dex增加到elements数组中。差量的方式拿到patch.dex,开启新进程的服务TinkerPatchService,将patch.dex与应用中的classes.dex合并,得到一个新的fix_classess.dex。通过反射操作得到PathClassLoader的DexPatchList,再反射调用patchlist的makeDexElements()方法,把fix_classess.dex直接替换到Element[]数组中去,达到修复的目的。从而提高了兼容性和稳定性。

(3)Instand Run 方案

Instant Run,是android studio2.0新增的一个运行机制,用来减少对当前应用的构建和部署的时间。

构建项目的流程:

构建修改的部分 → 部署修改的dex或资源 → 热部署,温部署,冷部署。

热拔插:方法实现的修改,或者变量值修改,不需要重启应用,不需要重建当前activity。

温拔插:代码修改涉及到了资源文件,activity需要被重启。

冷拔插:修改了继承规则、修改了方法签名,app需要被重启,但是仍然不需要重新安装 。

Android 热修复原理,DVM或ART与JVM的介绍ClassLoad及双亲委派模型理解

Android 热修复开源方案阿里、微信、美团等

五、总结

模块化、组件化、插件化通讯方式不同之处

  1. 模块化相互引入,抽取了公共的common模块,其他模块自然要引入这个module。
  2. 组件化主流是隐式和路由。隐式使解耦和灵活大大降低,因此路由是主流。
  3. 插件化本身是不同进程,因此是binder机制进程间通讯。

     

更多相关文章

  1. ubuntu 10.10 64Bit下编译android和android SDK
  2. android studio 适配android7.0 android 6.0拍照调用系统裁剪工
  3. Android热修复技术怎么选
  4. Android程序签名详解、打包,分别使用keytool工具和Android(安卓)S
  5. 阿里云栖大会 app加固小记
  6. Android(安卓)系统拍照及打开系统相册 完美适配 Android(安卓)4
  7. Android(安卓)ROM开发(二)——ROM架构以及Updater-Script脚本分析,
  8. android 自定义复合控件
  9. Android(安卓)桌面组件【app widget】 进阶项目②--心情记录器

随机推荐

  1. HttpEntity的使用 .
  2. Android(安卓)控件开发之ToggleButton
  3. Android Gallery 画廊Demo
  4. Android 登陆界面及记住用户名密码
  5. Android调用系统自定义设置界面
  6. android简单倒计时
  7. Android : 简单login
  8. Android(安卓)首个Activity启动动画设置
  9. Android 自定义布局的Toast
  10. Android(安卓)Studio操作/问题积累