android使用美团的robuts对app进行热修复

网上借鉴的对比图,也是最终尝试robust的原因,robust可以增加类和方法,demo未测试增加类.据说资源方便也在测试中了

android使用美团的robuts对app进行热修复_第1张图片

robuts修复原理:github地址 

1.在项目跟目录的build.gradle文件中增加以下代码

android使用美团的robuts对app进行热修复_第2张图片

2.在宿主程序(app目录)的build.gradle文件中添加一下代码

android使用美团的robuts对app进行热修复_第3张图片

//apply plugin: 'auto-patch-plugin'
生成补丁文件时取消代码注释,需要放在除com.android.application其他的plugin之前.

3.在app目录下(src同级目录)创建必要的robuts目录和robuts.xml文件

android使用美团的robuts对app进行热修复_第4张图片

robuts.xml 重点修改两个地方:1.packname 需要修复文件的目录;2.patchpackname:要与patchmaninpulate实现类中设置setpatchesInfoImplClassFullName方法参数中的包名一样,类名默认都是PathesInfoImpl

xml version="1.0" encoding="utf-8"?>


    
        
        
        true
        

        
        
        
        false

        
        
        
        false

        
        true
        

        
        
        false

        
        true
        

        
        true
        
    

    
    
    name="hotfixPackage">
        
        
        
        
        com.example.huangxiaoyu.hotfixtest
    

    
    name="exceptPackage">
        com.meituan.robust
        com.meituan.sample.extension
    

    
    name="patchPackname">
        com.example.huangxiaoyu.hotfixtest.patch
    

    
    name="classes no need to reflect">

    

以上准备工作完成后开始打包修复前的apk,案例apk代码值演示robuts热修复功能,代码简单.2个页面.A页面跳转B页面,A页面两个按钮.一个加载修复后的打包文件.一个跳转到B页面.B页面根据修复补丁前后显示内容不同

android使用美团的robuts对app进行热修复_第5张图片

mainactivity主要代码

findViewById(R.id.btn_upload).setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {//加载补丁
        new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new CallBack()).start();
    }
});
findViewById(R.id.btn_action).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(MainActivity.this, Activity_Second.class));
    }
});

secondactivity修复前

public class Activity_Second extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        TextView tv_text = findViewById(R.id.tv_text);
        tv_text.setText(getString());
    }

    String getString() {
        return "修复前";
    }

}

patchmanipulateimp


public class PatchManipulateImp extends com.meituan.robust.PatchManipulate {
    @Override
    protected List 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);
        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
        patch.setLocalPath(context.getApplicationContext().getFilesDir().getAbsolutePath() + File.separator + "patch");
        /*上面的路径看似设置的是目录,其实不是,get方法中默认追加了.jar;temp默认则追加_temp.jar.可以理解为设置补丁的文件名.建议放在程序内部目录,提高安全性*/
        /*com.example.huangxiaoyu.hotfixtest.patch 要和robutspatchPackname节点里面的值保持一样并且存在的目录*/
        patch.setPatchesInfoImplClassFullName("com.example.huangxiaoyu.hotfixtest.patch.PatchesInfoImpl");
        List patches = new ArrayList();
        patches.add(patch);
        return patches;
    }

    @Override

    protected boolean verifyPatch(Context context, Patch patch) {
        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;
    }

    @Override
    protected boolean ensurePatchExist(Patch patch) {
        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();
        }
    }


}

callback代码就是空实现

public class CallBack  implements RobustCallBack {
    @Override
    public void onPatchListFetched(boolean result, boolean isNet, List patches) {
        Log.d("RobustCallBack", "onPatchListFetched result: " + result);
        Log.d("RobustCallBack", "onPatchListFetched isNet: " + isNet);
        for (Patch patch : patches) {
            Log.d("RobustCallBack", "onPatchListFetched patch: " + patch.getName());
        }
    }

    @Override
    public void onPatchFetched(boolean result, boolean isNet, Patch patch) {
        Log.d("RobustCallBack", "onPatchFetched result: " + result);
        Log.d("RobustCallBack", "onPatchFetched isNet: " + isNet);
        Log.d("RobustCallBack", "onPatchFetched patch: " + patch.getName());
    }

    @Override
    public void onPatchApplied(boolean result, Patch patch) {
        Log.d("RobustCallBack", "onPatchApplied result: " + result);
        Log.d("RobustCallBack", "onPatchApplied patch: " + patch.getName());

    }

    @Override
    public void logNotify(String log, String where) {
        Log.d("RobustCallBack", "logNotify log: " + log);
        Log.d("RobustCallBack", "logNotify where: " + where);
    }

    @Override
    public void exceptionNotify(Throwable throwable, String where) {
        Log.e("RobustCallBack", "exceptionNotify where: " + where, throwable);
    }
}

mineapplication中对补丁进行启动加载.测试中发现如果不在application中加载,程序重启后修复内容失效.

android使用美团的robuts对app进行热修复_第6张图片

以上完成后执行打包命令:

gradlew clean assembleRelease --stacktrace --no-daemon

注:如果打包命令不能执行,需配置环境变量path,增加adb正确路径,打完包后会安装生成的apk文件到手机.并运行软件

补丁前完成

把下面对应的mapping.txt和methoodsmap.robuts文件复制到app目录下(src同级目录)下的robuts目录

android使用美团的robuts对app进行热修复_第7张图片                           复制后 android使用美团的robuts对app进行热修复_第8张图片

修改seondactivity,修改的方法或者类需要添加@modify注解,添加的类和方法需要添加@add注解,否则不能正常打补丁.

android使用美团的robuts对app进行热修复_第9张图片

在app的build.gradle文件里面取消注释auto-patch-plugin

android使用美团的robuts对app进行热修复_第10张图片

4.再次执行执行打包命令

gradlew clean assembleRelease --stacktrace --no-daemon*/

第二次次打包会抛出auto patch end successfully表示补丁已经生成,生异常日志如下:

android使用美团的robuts对app进行热修复_第11张图片

在如下目录找到patch.jar,吧这个文件push到patchmanipulateimp类中设置的路径中去,注意路径的地址,其实代码是设置一个文件的路径.并不是设置补丁文件的目录,需要把文件push到最后一个字符串的前一个目录级别中去

android使用美团的robuts对app进行热修复_第12张图片 本案例push文件的地址android使用美团的robuts对app进行热修复_第13张图片

push之后在打开的app中点击上面的hellword按钮加载补丁.然后在点击第二helloworld按钮,补丁就能看到B页面补丁后显示的内容:"修复后后后".

重启程序后会在application中启动加载补丁. 如果删除补丁文件,则程序重启后补丁失效.

完成修复

实际应用中可以根据当前的程序版本下载不同补丁包.每次补丁替换patch.jar文件.下载后加载一下补丁.下次程序再次启动会按照application中的设置自动加载补丁

你可能感兴趣的:(android)