robust是美团开发的一套热更新方案,它是基于Instant Run原理开发的一套新框架,相比于Tinker这种改变DexElements加载顺序的方式只能重启生效,robust实现了对代码修改的实时生效(热插拔)。下面来说说Robust的使用。

1.添加依赖

在project中的build.gralde中添加依赖

dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
        //编译apk使用使用
        classpath 'com.meituan.robust:gradle-plugin:0.4.90'
       //打更新包时使用
        classpath 'com.meituan.robust:auto-patch-plugin:0.4.90'
    }

在app的build.gradle中添加依赖

apply plugin: 'robust'
//当需要打更新包的时候打开
//apply plugin: 'auto-patch-plugin'
 ....
implementation 'com.meituan.robust:robust:0.4.90'

2.配置robust.xml

robust使用的时候需要配置一个robust.xml的配置文件,和src目录同级。

<?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.app.motion.robustmotion</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.app.motion.robustmotion.patch</name>
    </patchPackname>

    <!--自动化补丁中,不需要反射处理的类,这个配置项慎重选择-->
    <noNeedReflectClass name="classes no need to reflect">

    </noNeedReflectClass>
</resources>

这里需要配置的主要是两个节点,hotfixpackagepatchpacknamehotfixpackage元素用于标识需要进行热更新类所属的包,也就是在这个包下面的类才可以进行热更新,否则不能。patchpackname用于标识配置更新包的包名,可以取任意的名字。

3.主体代码

demo的布局比较简单,只有两个button,一个用于加载更新包,一个用于跳转到SecondActivity。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mBtnLoadPatch;
    private Button mBtnSecond;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnLoadPatch = this.findViewById(R.id.btn_loadpatch);
        mBtnSecond = this.findViewById(R.id.btn_second);
        mBtnLoadPatch.setOnClickListener(this);
        mBtnSecond.setOnClickListener(this);

        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x001);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.btn_loadpatch) {
            new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new Callback()).start();
        }
        if (view.getId() == R.id.btn_second) {
            startActivity(new Intent(this, SecondActivity.class));
        }

    }
}

class PatchManipulateImp extends PatchManipulate {
    @Override
    protected List<Patch> fetchPatchList(Context context) {
        Patch patch = new Patch();
        patch.setName("123");
        //设置pacth包的地址
        patch.setLocalPath(Environment.getExternalStorageDirectory().getPath() + File.separator + "robust" + File.separator + "patch.jar");
        patch.setTempPath(Environment.getExternalStorageDirectory().getPath() + File.separator + "robust" + File.separator + "patch");
        //这个包必须和patchpackname设置的要一致,patchpackname + '.pachesInfoImpl'
        patch.setPatchesInfoImplClassFullName("com.app.motion.robustmotion.patch.PatchesInfoImpl");
        List patches = new ArrayList<Patch>();
        patches.add(patch);
        return patches;
    }

    @Override
    protected boolean verifyPatch(Context context, Patch patch) {
        return true;
    }

    @Override
    protected boolean ensurePatchExist(Patch patch) {
        return true;
    }
}


class Callback implements 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) {

    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_second);

        mTextView = this.findViewById(R.id.tv_content);
        mTextView.setText(getContentString());
    }

    private String getContentString() {
        String result = "hello world";
        return result;
    }

}

使用命令打release包,注意robust只用在打release包时候才有效。gradlew clean assembleRelease

device-2019-11-24-161724.png

4.打补丁包

现在需要对SecondActivity进行一些修改

public class SecondActivity extends AppCompatActivity {

    private TextView mTextView;

    @Override
    @Modify
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_second);

        mTextView = this.findViewById(R.id.tv_content);
        //修改内容
        mTextView.setText(getNewContent());
    }

    private String getContentString() {
        String result = "hello world";
        return result;
    }


    @Add
    private String getNewContent() {
        return "hello my robust";
    }
}

比较重要的地方是,对于新增的方法需要使用@Add注解标记,对于引用的方法也需要使用@Modify注解标记.

然后需要将第一次打包之后生成的outputs/mapping/release/mapping.txt和outputs/robust/methodsMap.robust两个文件复制到src同目录下的robust目录下(没有新建一个)。

最后一步是打开app的build.gradle中的生成patch的插件。

apply plugin: 'com.android.application'
//apply plugin: 'robust'
apply plugin: 'auto-patch-plugin'

使用命令重新打包一次,gradlew assembleRelease

Execution failed for task ':app:transformClassesWithAutoPatchTransformForRelease'.
> auto patch end successfully

打包失败也不要紧,只要有auto patch end successfully显示表示patch包打包成功

现在只需要把outputs/robust/patch.jar复制到指定的目录即可adb push E:\github_workspace\RobustMotion\app\build\outputs\robust\patch.jar /sdcard/robust/patch_temp.jar

device-2019-11-24-163248.png

demo代码地址:https://gitee.com/devnew/Robu...


summerpxy
44 声望1 粉丝