美文网首页
Robust使用篇

Robust使用篇

作者: A邱凌 | 来源:发表于2020-01-06 16:46 被阅读0次

Robust使用篇

Robust使用相对还是比较简单的.有一些坑可能官方文档里讲的不是那么详细,需要自己踏过去

使用方法

  • 在project build.gradle 下buildscript -> dependencies 加入以下代码

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.meituan.robust:gradle-plugin:0.4.91'
        classpath 'com.meituan.robust:auto-patch-plugin:0.4.91'
        #当前最新版本为0.4.91
    }
    
  • 在app的build.gradle中加入以下代码,然后sync

    apply plugin: 'com.android.application'
        //下面这个是制作补丁时使用,暂时先注释掉
        //apply plugin: 'auto-patch-plugin'
    apply plugin: 'robust'
    
  • 在app目录下创建robust目录(留着后面备用),创建robust.xml,加入以下代码,可以根据自己的项目进行配置,注释已经写得很明白了.

    <?xml version="1.0" encoding="utf-8"?>
            <resources>
    
    <switch>
        <!--true代表打开Robust,请注意即使这个值为true,Robust也默认只在Release模式下开启-->
        <!--false代表关闭Robust,无论是Debug还是Release模式都不会运行robust-->
        <turnOnRobust>true</turnOnRobust>
        <!--<turnOnRobust>false</turnOnRobust>-->
    
        <!--是否开启手动模式,手动模式会去寻找配置项patchPackname包名下的所有类,自动的处理混淆,然后把patchPackname包名下的所有类制作成补丁-->
        <!--这个开关只是把配置项patchPackname包名下的所有类制作成补丁,适用于特殊情况,一般不会遇到-->
        <!--<manual>true</manual>-->
        <manual>false</manual>
    
        <!--是否强制插入插入代码,Robust默认在debug模式下是关闭的,开启这个选项为true会在debug下插入代码-->
        <!--但是当配置项turnOnRobust是false时,这个配置项不会生效-->
        <!--<forceInsert>true</forceInsert>-->
        <forceInsert>false</forceInsert>
    
        <!--是否捕获补丁中所有异常,建议上线的时候这个开关的值为true,测试的时候为false-->
        <catchReflectException>true</catchReflectException>
        <!--<catchReflectException>false</catchReflectException>-->
    
        <!--是否在补丁加上log,建议上线的时候这个开关的值为false,测试的时候为true-->
        <!--<patchLog>true</patchLog>-->
        <patchLog>false</patchLog>
    
        <!--项目是否支持progaurd-->
        <proguard>true</proguard>
        <!--<proguard>false</proguard>-->
    
        <!--项目是否支持ASM进行插桩,默认使用ASM,推荐使用ASM,Javaassist在容易和其他字节码工具相互干扰-->
        <useAsm>true</useAsm>
        <!--<useAsm>false</useAsm>-->
    </switch>
    
    <!--需要热补的包名或者类名,这些包名下的所有类都被会插入代码-->
    <!--这个配置项是各个APP需要自行配置,就是你们App里面你们自己代码的包名,
    这些包名下的类会被Robust插入代码,没有被Robust插入代码的类Robust是无法修复的-->
    <packname name="hotfixPackage">
        <name>com.dsg.robustdemo</name>
    </packname>
    
    <!--不需要Robust插入代码的包名,Robust库不需要插入代码,如下的配置项请保留,还可以根据各个APP的情况执行添加-->
    <exceptPackname name="exceptPackage">
        <name>com.meituan.robust</name>
        <name>com.meituan.sample.extension</name>
    </exceptPackname>
    
    <!--补丁的包名,请保持和类PatchManipulateImp中fetchPatchList方法中设置的补丁类名保持一致( setPatchesInfoImplClassFullName("com.meituan.robust.patch.PatchesInfoImpl")),
    各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是如下的配置项,类名必须是:PatchesInfoImpl-->
    <patchPackname name="patchPackname">
        <name>com.dsg.robustdemo</name>
    </patchPackname>
    
    <!--自动化补丁中,不需要反射处理的类,这个配置项慎重选择-->
    <noNeedReflectClass name="classes no need to reflect">
    
    </noNeedReflectClass>
    

</resources>
```

  • 打一个release包,将build -> outputs -> mapping -> mapping.txt 和build -> outputs ->apk -> release -> app-release.apk 和 将build -> outputs ->robust 下的文件 一起复制到我们之前创建的robust目录下.这时候我们反编译一下apk,看看robust都对我们做了什么操作


    robust反编译apk.png

    我们可以看到,多了一个ChangeQuickRedirect对象,每个方法都加上了PatchProxy.proxy方法,这个具体后面会讲,Robust就是通过这种方式来判断是加载patch中的方法还是使用原方法来实现热修复

  • 然后修改我们的源码,将需要修改的bad code 加上@Modify注解,新增的方法或变量加入@Add注解,将app下的build.gradle中auto-patch-plugin 插件打开,记得一定要将robust插件注释掉,如下

    apply plugin: 'auto-patch-plugin'
    //apply plugin: 'robust'
    
    
  • 然后删掉build文件夹,重新打正式包,这时候我们遇到failed,少年人,不要慌.正常正常.
    只要你遇到auto patch end successfully就表示已经将插件包打包成功了,如下图所示

    autoPatch.png
  • 将outputs下的patch.jar复制到我们自己创建的robust文件夹,这个patch.jar就是我们的补丁包,我们需要将这个放在服务器上,再适当的时候进行下发,我们模拟一下下发服务器的操作,我们可以通过adb push 将patch.jar 放到我们手机的的robust文件夹(这个文件夹可以自定义,需要和下面代码中的对应),然后新建如下类

package com.dsg.robustdemo;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.meituan.robust.Patch;
import com.meituan.robust.PatchManipulate;
import com.meituan.robust.RobustApkHashUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by mivanzhang on 17/2/27.
 *
 * We recommend you rewrite your own PatchManipulate class ,adding your special patch Strategy,in the demo we just load the patch directly
 *
 * <br>
 *   Pay attention to the difference of patch's LocalPath and patch's TempPath
 *
 *     <br>
 *    We recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar
 *<br>
 *<br>
 *    我们推荐继承PatchManipulate实现你们App独特的A补丁加载策略,其中setLocalPath设置补丁的原始路径,这个路径存储的补丁是加密过得,setTempPath存储解密之后的补丁,是可以执行的jar文件
 *     <br>
 *     setTempPath设置的补丁加载完毕即刻删除,如果不需要加密和解密补丁,两者没有啥区别
 */

public class PatchManipulateImp extends PatchManipulate {
    /***
     * connect to the network ,get the latest patches
     * l联网获取最新的补丁
     * @param context
     *
     * @return
     */
    @Override
    protected List<Patch> fetchPatchList(Context context) {
        //将app自己的robustApkHash上报给服务端,服务端根据robustApkHash来区分每一次apk build来给app下发补丁
        //apkhash is the unique identifier for  apk,so you cannnot patch wrong apk.
        String robustApkHash = RobustApkHashUtils.readRobustApkHash(context);
        Log.w("robust","robustApkHash :" + robustApkHash);
        //connect to network to get patch list on servers
        //在这里去联网获取补丁列表
        Patch patch = new Patch();
        patch.setName("123");
        //we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar
        //LocalPath是存储原始的补丁文件,这个文件应该是加密过的,TempPath是加密之后的,TempPath下的补丁加载完毕就删除,保证安全性
        //这里面需要设置一些补丁的信息,主要是联网的获取的补丁信息。重要的如MD5,进行原始补丁文件的简单校验,以及补丁存储的位置,这边推荐把补丁的储存位置放置到应用的私有目录下,保证安全性
        patch.setLocalPath(Environment.getExternalStorageDirectory().getPath()+ File.separator+"robust"+ File.separator + "patch");

        //setPatchesInfoImplClassFullName 设置项各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是和xml配置项patchPackname保持一致,而且类名必须是:PatchesInfoImpl
        //请注意这里的设置
        patch.setPatchesInfoImplClassFullName("com.dsg.robustdemo.PatchesInfoImpl");
        List patches = new ArrayList<Patch>();
        patches.add(patch);
        return patches;
    }

    /**
     *
     * @param context
     * @param patch
     * @return
     *
     * you can verify your patches here
     */
    @Override

    protected boolean verifyPatch(Context context, Patch patch) {
        //do your verification, put the real patch to patch
        //放到app的私有目录
        patch.setTempPath(context.getCacheDir()+ File.separator+"robust"+ File.separator + "patch");
        //in the sample we just copy the file
        try {
            copy(patch.getLocalPath(), patch.getTempPath());
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("copy source patch to local patch error, no patch execute in path "+patch.getTempPath());
        }

        return true;
    }
    public void copy(String srcPath, String dstPath) throws IOException {
        File src=new File(srcPath);
        if(!src.exists()){
            throw new RuntimeException("source patch does not exist ");
        }
        File dst=new File(dstPath);
        if(!dst.getParentFile().exists()){
            dst.getParentFile().mkdirs();
        }
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(dst);
            try {
                // Transfer bytes from in to out
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }
    /**
     *
     * @param patch
     * @return
     *
     * you may download your patches here, you can check whether patch is in the phone
     */
    @Override
    protected boolean ensurePatchExist(Patch patch) {
        return true;
    }
}
  • 这时候我们就已经相当于将patch.jar下发到手机中,并且加载代码已经准备好,接下来只需要加载一下patch.jar,就可以实现热修复,加载方法如下

     new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new RobustCallBack() {
                    @Override
                    public void onPatchListFetched(boolean result, boolean isNet, List<Patch> patches) {
    
                    }
    
                    @Override
                    public void onPatchFetched(boolean result, boolean isNet, Patch patch) {
    
                    }
    
                    @Override
                    public void onPatchApplied(boolean result, Patch patch) {
    
                    }
    
                    @Override
                    public void logNotify(String log, String where) {
    
                    }
    
                    @Override
                    public void exceptionNotify(Throwable throwable, String where) {
    
                    }
                }).start();
    
    
  • 到现在为止,整个robust热修复就已经完成了.

总结一下robust的优缺点

  • 优点
    • 兼容性好,因为没有hook系统的函数,我们也不需要对jvm进行适配,还参考了instant run,所以兼容性很好
    • 实时热修复,只要patch.jar下发,重新加载就可以使用新方法,不需要重启应用
  • 缺点
    • 增加包体积,因为每个方法都需要插入上面的代码
    • 不支持so和资源替换,不过美团文档中说已经在内测中

参考美团文档

相关文章

网友评论

      本文标题:Robust使用篇

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