美文网首页
安卓代码修复框架Patch(1)

安卓代码修复框架Patch(1)

作者: 呵呵_9e25 | 来源:发表于2019-07-25 15:12 被阅读0次

安卓代码修复框架Patch

只支持程序重启修复,不支持热修复

markdown

代码原理

  • 1.主要通过反正把原来dexElements数组替换成我们新的dexElements数组,我们新的dexElements数组主要包含了我们修复的代码类,安卓加载dex里面的类有个特点就是优先被加载到虚拟机的类,如果后面出现相同签名的类文件将不进行处理,相当于先到先得。

  • 2.于是我们利用了它的这个特点只要实现我们修改好的类比我们出现bug的类先给虚拟机加载就可以了。

  • 3.但是对于多dex还有一个特点就是android5.0开始开启了ISPRIVILEAGE校验,啥意思,就是如果一个dex里的类没有依赖于其他dex文件的类,那就会打上这个标签,一旦打上这个标签,其他dex文件是不能访问的,那对于我们代码修复就有问题了,如果我们要修复的类是没有依赖于其他类的,但是我们修复的时候把他打到第一个dex文件中,那就会出现问题,为了避免这个问题,我们最好把所有的类都去掉这个标签。怎么去掉,很简单,让所有的类都存在同一个类的应用,然后无论有多少个dex都必定引用我们第一个dex的这个类,这样就能有效的去除这个标签的验证。

  • 4.如何给每个类添加一个类的应用,我们选择用gradle插件,在dex任务开始之前,在dex任务获取所有可用依赖任务之后,进行代码插入,
    为什么要在这个时候呢,因为这个过程中class文件会被转化成dex文件,这是class最后存在的时机,也相当于最后形态,不会出现任何修改class的行为了,所以在这里对class进行处理是最好的

  • 5.具体就是用ASM对每个类的构造函数注入一个我们Hack类的应用,类似于Class hack=Hack.class;这样的格式,我们可以利用Android studio插件show bytecode outline显示出ASM代码,具体代码为 mv.visitLdcInsn(Type.getType("Lcom/example/administrator/patchdemo/Hack;"));这样就能在每个类的构造函数注入我们第一个dex文件的一个Hack类的引用

  • 6.然后我们会为每个需要打包的类计算它的hash值,因为每个类的hash值都是唯一的,那么我们每次修改了一个类,对比上一个hash文件,就能知道哪些类发生了修改,那我们就知道要把哪些文件打入补丁包里面,这个原理,在美团的robust热修复框架也有对应的应用

代码实现

1.自定义生成补丁的gradle 插件

自定义gradle插件,网上有很多文章,我这边用的是独立的库方式。

具体步骤

1.新建一个module我这边选择的是 javalibrary

image.png
image.png

然后生成这样的目录

image.png
我们在java同级目录新建一个目录叫做 groovy
image.png
然后我们还需要做的是新建一个resources目录,同样在 java同级目录
image.png
然后我们开始我们插件开发
  • 1.新建一个groovy文件
    当然我们可以增加我们的包目录路径,类似我这边的com.bigman.tech,然后我们新建一个类叫做PatchPlugin
    image.png
    当然这个文件是以.groovy结尾的
    然后我们开始开发我们的插件
class PatchPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
    }
}

代码实现如上

  • 2.下一步我们要声明一下我们的插件名称
    我们找到刚刚新建的resources目录,我们继续新建两级目录,和一个文件
    image.png

这两级目录名称固定不能变,然后是我们的properties文件,文件名就是我们以后用到app里面的名字,比如我们这里到时候会这么用apply plugin: 'com.bigman.tech',这里的名字决定了我们使用时候的名字
然后我们看一下这个文件里面有啥

implementation-class=com.bigman.tech.PatchPlugin

主要就是我们刚新建的groovy文件里面类的全路径名称,这里千万不能写错

  • 3.第三步我们要配置一个发布插件的脚本
    找到我们刚新建的java library里面的build.gradle
    我们改成这样
//应用groovy插件
apply plugin: 'groovy'
//要发布插件,必须应用这个maven插件,当然还有其他第三方插件
apply plugin: 'maven'


repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    compile gradleApi()
    compile 'com.android.tools.build:gradle:1.5.0'
    compile 'commons-io:commons-io:2.4'
    compile 'commons-codec:commons-codec:1.11'
    compile 'org.ow2.asm:asm-all:5.0.3'
}

sourceCompatibility = "7"
targetCompatibility = "7"


group="com.bigman"
archivesBaseName='patch-gradle'
version ='1.0.0'


uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}

主要是这三个配置

group="com.bigman"
archivesBaseName='patch-gradle'
version ='1.0.0'

平时我们应用依赖的时候就是通过group+archivesBaseName+version来进行寻找的
看一下我们怎么使用这个插件
首先我们找到项目根目录下面的build.gradle文件,然后我们在dependencies加入一个classpath

buildscript {
    
    repositories {
        google()
        jcenter()
        maven { url uri('repo') }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath 'com.bigman:patch-gradle:1.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

值得注意的还有一个地方,就是我们的repositories我们还加了一个maven { url uri('repo') }这个是让我们脚本去我们本地的repo目录去需要我们的依赖库,比如我们这个插件
但这个具体是怎么配置到本地maven库目录的,就是我们上面插件build.gradle文件里面有一个uploadArchives任务,这是maven插件提供的一个上传库的任务

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}

我们配置了让库上传到上级目录的一个repo目录下面,然后我们找到我们AS编辑器右边有一个gradle图标打开找到我们的 插件模块,比如我这边的plugin模块里面有一个upload任务

image.png
双击之后我们就可以在项目目录下面找到一个文件夹,里面就是我们刚上传的插件库
image.png
现在算是初步完成了自定义插件,我们可以试试有没有用
  • 4.测试 可以先在我们项目根目录依赖我们的插件,就像上面说的那样,然后找到我们的主module的build.gradle应用一下我们的插件就行,apply plugin: 'com.bigman.tech',然后构建一下你的app项目,结果发现没啥反应,其实没啥反应是最好的反应。如果报错了,我们还要去调试,调试插件我的另一篇文章里有说,可以具体看看。
    我们还是让他有点反应,怎么弄,很简单,打印一句话就行
    我们改一下代码
class PatchPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
  println ("我是插件")
    }
}

然后我们重新点击一下AS右侧的上传任务,重新上传一下,然后我们在这里找到app的assemble任务,双击运行看一下有没有打印日志就行,试试看


image.png

这里上传插件成功


ze'h
这里是插件应用成功,至此我们开发自定义插件的第一步已经成功迈出

相关文章

网友评论

      本文标题:安卓代码修复框架Patch(1)

      本文链接:https://www.haomeiwen.com/subject/mhmcrctx.html