热更新——Sophix

牢骚

前些时间,公司因为服务器调整,有些特定接口IP需要更换,其中包括一个更新接口。从接到通知更换服务器到更换完成,前端开发人员只有两个小时的准备时间,其中一个小时我还在来公司的路上(晚上十点接到通知)
两个小时内及时发包也不能保证用户的更新,况且还是在晚上。
所以这几天一直很迫切需要一款热更新框架,Sophix就出现了。

介绍

官方文档

Sophix是阿里爸爸推出的第三款热更新开源框架,这里有官方给的数据对比:

方案对比 Andfix开源版本 阿里Hotfix 1.X 阿里Hotfix最新版 (Sophix)
方法替换 支持,除部分情况 支持,除部分情况 全部支持
方法增加减少 不支持 不支持 以冷启动方式支持
方法反射调用 只支持静态方法 只支持静态方法 以冷启动方式支持
即时生效 支持 支持 视情况支持
多DEX 不支持 支持 支持
资源更新 不支持 不支持 支持
so库更新 不支持 不支持 支持
Android版本 支持2.3~7.0 支持2.3~6.0 全部支持包含7.0以上
已有机型 大部分支持 大部分支持 全部支持
安全机制 加密传输及签名校验 加密传输及签名校验
性能损耗 低,几乎无损耗 低,几乎无损耗 低,仅冷启动情况下有些损耗
生成补丁 繁琐,命令行操作 繁琐,命令行操作 便捷,图形化界面
补丁大小 不大,仅变动的类 小,仅变动的方法 不大,仅变动的资源和代码
服务端支持 支持服务端控制 支持服务端控制

看着就感觉非常强大,最吸引我的还是便捷,图形化界面,让我这种脑子不够用的最喜欢了。

下面介绍的集成过程,只是相对官网简化一些,添加一些集成过程中踩到的坑。
如果想要更加详细的集成文档,请点击官方文档

集成准备

一、添加产品

  • 首先进入阿里云管理控制台,点击产品与服务,在移动服务栏点击移动热修复。
热更新——Sophix_第1张图片
添加产品步骤

阿里爸爸需要同意的,就同意吧……

  • 点击添加产品,名称随意,与热更新的项目无关。
热更新——Sophix_第2张图片
添加产品
  • 点击添加好的产品,进入后添加应用,应用名可以随意,但包名必须与热更新的项目统一。

添加好后,界面如下:

热更新——Sophix_第3张图片
添加应用后界面
  • 下载aliyun-emas-services.json文件
    后面配置需要的数据,都在该文件中

二、SDK引入

SDK引入有以下两种方式,官方更推荐第一种。

1. gradle远程仓库依赖

添加maven仓库地址

repositories {
   maven {
       url "http://maven.aliyun.com/nexus/content/repositories/releases"
   }
}

在app.gradle中添加依赖

/*
    Android Studio 3.0以后,官方推荐使用  implementation 或 api 添加依赖
    如果是之前的版本,使用compile
 */
implementation 'com.aliyun.ams:alicloud-android-hotfix:3.2.3'

2. SDK下载

SDK下载地址

三、添加必要权限







四、添加meta-data

AndroidManifest.xml文件的Application节点下面,添加以下内容:


    

    

这三个值需要到前面下载的aliyun-emas-services.json文件里查找,对应关系如下:

value名 对应字段
App ID hotfix.idSecret
App Secret emas.appSecret
RSA密钥 hotfix.rsaSecret

不用怀疑,RSA秘钥就是这么长

官方文档建议,使用setSecretMetaData这个方法进行设置。后面代码配置中会讲到。

五、混淆配置(可选)

#基线包使用,生成mapping.txt
-printmapping mapping.txt
#生成的mapping.txt在app/build/outputs/mapping/release路径下,移动到/app路径下
#修复后的项目使用,保证混淆结果一致
#-applymapping mapping.txt
#hotfix
-keep class com.taobao.sophix.**{*;}
-keep class com.ta.utdid2.device.**{*;}
#防止inline
-dontoptimize

这样,前期的配置准备就完成了。将项目编译以下,准备后面的代码配置。

代码配置

这里我直接贴上初始化的代码,在注释中详细介绍:

public class App extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        initSophix();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        /*
            queryAndLoadNewPatch不可放在attachBaseContext 中,
            否则无网络权限,建议放在后面任意时刻,如onCreate中
          */
        SophixManager.getInstance().queryAndLoadNewPatch();
    }

    /**
     * 初始化Sophix
     * 需要在attachBaseContext方法里面调用
     * 并且要在super.attachBaseContext(base);和Multidex.install方法之后调用
     * 且在其他方法之前
     */
    private void initSophix() {
        String appVersion = "1.0";
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            appVersion = packageInfo.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        SophixManager.getInstance().setContext(this)
                /*
                    设置版本号,版本号与控制台的版本号统一,才可以更新
                    这里我踩的坑,控制台上添加版本,是添加需要更新的版本,与版本升级没有关系
                 */
                .setAppVersion(appVersion)
                //<可选>用户自定义aes秘钥, 会对补丁包采用对称加密
                .setAesKey(null)
                /*
                    <可选> isEnabled默认为false, 是否调试模式, 调试模式下会输出日志以及不进行补丁签名校验.
                    线下调试此参数可以设置为true, 查看日志过滤TAG
                    正式发布必须改为false,否则存在安全风险
                 */
                .setEnableDebug(true)
                /*
                     <可选,推荐使用> 三个Secret分别对应AndroidManifest里面的三个,
                     可以不在AndroidManifest设置而是用此函数来设置Secret
                 */
                .setSecretMetaData(null, null, null)
                /*
                     <可选> 设置patch加载状态监听器,
                    该方法参数需要实现PatchLoadStatusListener接口
                 */
                .setPatchLoadStatusStub(new PatchLoadStatusListener() {
                    @Override
                    public void onLoad(int mode, int code, String info, int handlePatchVersion) {
                        // 补丁加载回调通知
                        Log.e("sophix", "onLoad: 补丁加载回调通知  code = " + code);
                        if (code == PatchStatus.CODE_LOAD_SUCCESS) {
                            // 表明补丁加载成功
                            Log.e("sophix", "onLoad: 表明补丁加载成功");
                        } else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
                            // 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;
                            // 建议: 用户可以监听进入后台事件, 然后调用killProcessSafely自杀,以此加快应用补丁,详见1.3.2.3
                            Log.e("sophix", "onLoad: 表明新补丁生效需要重启. 开发者可提示用户或者强制重启");
                            SophixManager.getInstance().killProcessSafely();
                        } else {
                            // 其它错误信息, 查看PatchStatus类说明
                            Log.e("sophix", "onLoad: 其它错误信息, 查看PatchStatus类说明");
                        }
                    }
                }).initialize();
    }
}

上面描述有够详细吧。这里贴上code的各个返回码含义:

//兼容老版本的code说明
    int CODE_LOAD_SUCCESS = 1;//加载阶段, 成功
    int CODE_ERR_INBLACKLIST = 4;//加载阶段, 失败设备不支持
    int CODE_REQ_NOUPDATE = 6;//查询阶段, 没有发布新补丁
    int CODE_REQ_NOTNEWEST = 7;//查询阶段, 补丁不是最新的 
    int CODE_DOWNLOAD_SUCCESS = 9;//查询阶段, 补丁下载成功
    int CODE_DOWNLOAD_BROKEN = 10;//查询阶段, 补丁文件损坏下载失败
    int CODE_UNZIP_FAIL = 11;//查询阶段, 补丁解密失败
    int CODE_LOAD_RELAUNCH = 12;//预加载阶段, 需要重启
    int CODE_REQ_APPIDERR = 15;//查询阶段, appid异常
    int CODE_REQ_SIGNERR = 16;//查询阶段, 签名异常
    int CODE_REQ_UNAVAIABLE = 17;//查询阶段, 系统无效
    int CODE_REQ_SYSTEMERR = 22;//查询阶段, 系统异常
    int CODE_REQ_CLEARPATCH = 18;//查询阶段, 一键清除补丁
    int CODE_PATCH_INVAILD = 20;//加载阶段, 补丁格式非法
    //查询阶段的code说明
    int CODE_QUERY_UNDEFINED = 31;//未定义异常
    int CODE_QUERY_CONNECT = 32;//连接异常
    int CODE_QUERY_STREAM = 33;//流异常
    int CODE_QUERY_EMPTY = 34;//请求空异常
    int CODE_QUERY_BROKEN = 35;//请求完整性校验失败异常
    int CODE_QUERY_PARSE = 36;//请求解析异常
    int CODE_QUERY_LACK = 37;//请求缺少必要参数异常
    //预加载阶段的code说明
    int CODE_PRELOAD_SUCCESS = 100;//预加载成功
    int CODE_PRELOAD_UNDEFINED = 101;//未定义异常
    int CODE_PRELOAD_HANDLE_DEX = 102;//dex加载异常
    int CODE_PRELOAD_NOT_ZIP_FORMAT = 103;//基线dex非zip格式异常
    int CODE_PRELOAD_REMOVE_BASEDEX = 105;//基线dex处理异常
    //加载阶段的code说明 分三部分dex加载, resource加载, lib加载
    //dex加载
    int CODE_LOAD_UNDEFINED = 71;//未定义异常
    int CODE_LOAD_AES_DECRYPT = 72;//aes对称解密异常
    int CODE_LOAD_MFITEM = 73;//补丁SOPHIX.MF文件解析异常
    int CODE_LOAD_COPY_FILE = 74;//补丁拷贝异常
    int CODE_LOAD_SIGNATURE = 75;//补丁签名校验异常
    int CODE_LOAD_SOPHIX_VERSION = 76;//补丁和补丁工具版本不一致异常
    int CODE_LOAD_NOT_ZIP_FORMAT = 77;//补丁zip解析异常
    int CODE_LOAD_DELETE_OPT = 80;//删除无效odex文件异常
    int CODE_LOAD_HANDLE_DEX = 81;//加载dex异常
    // 反射调用异常
    int CODE_LOAD_FIND_CLASS = 82;
    int CODE_LOAD_FIND_CONSTRUCTOR = 83;
    int CODE_LOAD_FIND_METHOD = 84;
    int CODE_LOAD_FIND_FIELD = 85;
    int CODE_LOAD_ILLEGAL_ACCESS = 86;
    //resource加载
    public static final int CODE_LOAD_RES_ADDASSERTPATH = 123;//新增资源补丁包异常
    //lib加载
    int CODE_LOAD_LIB_UNDEFINED = 131;//未定义异常
    int CODE_LOAD_LIB_CPUABIS = 132;//获取primaryCpuAbis异常
    int CODE_LOAD_LIB_JSON = 133;//json格式异常
    int CODE_LOAD_LIB_LOST = 134;//lib库不完整异常
    int CODE_LOAD_LIB_UNZIP = 135;//解压异常
    int CODE_LOAD_LIB_INJECT = 136;//注入异常

补丁包生成

万事俱备,只欠补丁了。听说最强的武器就是补丁。

补丁生成器下载及介绍

这里就不多废话了,点击上面的链接,里面有很详细的介绍。
注意:两个APK之间一定要有差距啊!

补丁包上传

点击控制台的热修复
→添加版本
→输入版本号(这里版本号要与项目版本相同)
→上传补丁(选择上一步生成的补丁包)

热更新——Sophix_第4张图片
补丁包上传

扫码验证补丁(可选,官方推荐,稳定保证)

在正式发布补丁之前,官方推荐先通过测试。

点开补丁详情,在右上角,之后就是人性化(傻瓜式)操作了,不做赘述。


热更新——Sophix_第5张图片
扫码验证补丁

发布补丁

做了这么多事,其实补丁还在自己家呢。我们需要点击下方的新建发布,选择合适的发布方式,这里我选择的全量发布。

打印一下日志:

08-11 16:24:21.506 14363-14435/com.martin.sophixstudy E/sophix: onLoad: 补丁加载回调通知  code = 9
    onLoad: 其它错误信息, 查看PatchStatus类说明
08-11 16:24:21.746 14363-14448/com.martin.sophixstudy E/sophix: onLoad: 补丁加载回调通知  code = 100
    onLoad: 其它错误信息, 查看PatchStatus类说明
    onLoad: 补丁加载回调通知  code = 12
    onLoad: 表明新补丁生效需要重启. 开发者可提示用户或者强制重启

上面代码中,强制杀死了APP进程,现在重新打开,嗯~可以看到更新的内容了。

你可能感兴趣的:(热更新——Sophix)