热修复Sophix多渠道补丁的解决方案

    • 1 背景
    • 2 问题解决路途
    • 3 解决方案

1 背景

最近,把Sophix集成到了一个多渠道的项目里。但,第一次使用就遇坑了。代码里使用了BuildConfig.FLAVOR来判断当前属于哪个渠道,如:

有两个渠道:taobao和tianmao
Test.java类中使用了BuildConfig.FLAVOR:

if ("taobao".equals(BuildConfig.FLAVOR)){
    toast("abc");
} else {
    toast("hello");
}

现在需要把”abc”换成”xyz”。

更换后,想使用热修复来实现。大概步骤:

  1. 使用旧包打包时的mapping,生成新包(可以只生成一个渠道的包)
  2. 使用同一渠道的新旧包,生成补丁文件
  3. 上传补丁,灰度发布

在灰度发布时,发现taobao渠道APP已经修复。

而,tianmao渠道的APP,因为属于同一版本,肯定也会打补丁。但,没想到,toast的内容,不是原“hello”,却变成了“abc”。

如果使用tianmao渠道来生成补丁,那tianmao正常。但taobao渠道在补丁打完后,却变成了“hello”。

2 问题解决路途

后来咨询阿里技术支持,技术支持说使用白名单的方法。因为没找到mapping文件下的混淆映射关系,我就把BuildConfig类的原包名和类名加入了白名单:
com.ali.six.BuildConfig

结果,还是跟原来的效果一样。技术支持又提供思路:是不是被编译成了常量。
经过反编译发现:编译前的BuildConfig,在编译后没有了,代码中使用到BuildConfig里值的地方,都换成了常量值。

查看BuildConfig类后,发现此类是final类,内部都是成员变量,且都被public static final修饰。这样,编译后,使用它们的地方,肯定会变成常量值。

补丁,是通过比较新旧包的差异来生成的。所以,修改的代码,肯定属于差异范围,而这块代码中的BuildConfig.FLAVOR,一直是个常量值,由生成补丁的渠道决定。

这样,就解释了前面遇到的问题。

最后,使用版本升级的方式更新。

3 解决方案

但心有不甘,那么喜欢Sophix,就不能用了?通过技术支持提供的这些思路,还是找到了解决方法:把BuildConfig封装成一个类MyBuildConfig,让原来所有使用到BuildConfig的地方,改成使用这个类;并把这个类加入到白名单。具体做法:
1、 新建类MyBuildConfig(接口也行,视具体情况而定)
2、使用表达式,或方法,对外提供调用接口,如:MyBuildConfig.IS_FLAVOR_TAO_BAO。这个类,在taobao渠道编译后,这个类只会留下 a = “taobao”.equals(“taobao”)这个表达式。

public class MyBuildConfig {
    /**
     * 直接常量全部封装在类内,对外只提供方法、或表达式的变量
     * 不提供可直接使用的常量,原因是:
     *  public static final String FLAVOR = BuildConfig.FLAVOR;
     *  编译时,BuildConfig.FLAVOR是常量值,所有用到MyBuildConfig.FLAVOR的地方,也都会变成常量值,这样,还是影响到了代码
     */
    private static final String FLAVOR_TAO_BAO = "taobao";
    private static final String FLAVOR_TIAN_MAO = "tianmao";
    public static final boolean IS_FLAVOR_TAO_BAO = FLAVOR_TAO_BAO.equals(BuildConfig.FLAVOR);
}

3、根据基线包的mapping,找到MyBuildConfig类在混淆后的路径,加入到白名单,如:com.ali.six.c
4、生成补丁文件

这样,就保证了因渠道不一样而影响逻辑的代码。如果,修复代码的地方跟渠道没有关系,那就不必使用MyBuildConfig

你可能感兴趣的:(Android)