AndroidAndroid 热修复Android

阿里最新热修复框架sophix-3.1.6集成详解

2017-11-23  本文已影响317人  月下溪明

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布


本文更新于2017年11月20日

前言

关于sophix集成和使用,网上有了很多前辈写的博客。读了很多,感觉都不太详细和系统。所以自己尝试写sophix集成文章,本文包括四部分内容:

关于sophix的原理和与其他热修复框架的比较,戳官方文档

阿里手淘团队出书了,业界首部全方位系统介绍热修复原理书籍,从阿里Sophix方案开发过程入手权威解读!《深入探索Android热修复技术原理》
这本书建议读一读。


话不多说,集成开始:

控制台开通移动热修复

阿里云控制台的使用有点绕,要注意了,对照着一步一步来

阿里云热修复控制台地址

Ps:

如果自己进了阿里云官网首页,怎么找热修复: 鼠标滑到 菜单栏 【产品】,弹出的菜单,找到白色字体类别【移动云】,移动云 的子菜单里找到【移动热修复】

image.png

· 右上角登录,可以使用淘宝账号直接登录。注册一个也行。

· 左边 点击 立即开通。

没开通的,会跳转到一个页面,告知 【确认开通】。

确认开通后,跳转到控制台的移动热修复页面,酱紫的

移动热修复 控制台

Ps:

如果读者自己是通过点官网首页左上角的【控制台】,直接进入了【管理控制台】,那怎么进到移动热修复的控制台页面呢:看上面的截图,菜单栏的 【产品与服务】,是以首字母排列的。找Y类-【移动热修复】。点一下,就切换到移动热修复的管理了。

截图中 【创建App】是新开一个标签页,跳转到 [移动云] 控制台(Mobile Hub)去创建的,和当前处在的 [移动热修复] 控制台 不同,不要搞混。

结果:移动云- 产品列表页 结果:移动云 -产品信息页

Ps:

在本页的 应用列表的App都有 查看信息 选项,这里用不到它,因为没有我们需要的RSA密钥。

点击 【创建应用】,填入App名(最好和项目名称一致),应用类型 选 Android,填入packageName。 (bundleId是iOS的标识)

创建成功后,在下方的应用列表展示信息。

进入移动热修复有两种方法:
1.看上图,可以在当前移动云 产品信息页 ,点击 移动热修复标签,
2.可以关掉当前网页(还记得在移动热修复控制台【创建App】是新开一个标签页吗)这样也可以回到移动热修复的页面,再刷新一下。

第1种方法结果:


第1种方法结果

第2种方法结果:


第2种方法,图一
点击应用列表【管理】,进入图二
第2种方法,图二

总之,一定要在创建完产品和应用后,到 [移动热修复] 标签页,才能查看到AppId,AppSecret,RSA密钥。不要在移动云的产品处查看,那样你是看不到RSA密钥的。

关于 【管理控制台】 的更多使用详情, 戳这里


工程代码快速接入

gradle远程仓库依赖, 打开项目找到app的build.gradle文件,添加如下配置:
添加maven仓库地址:

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

添加gradle坐标版本依赖:

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

Sophix 3.1.6版本以后引入了新的初始化方式。

原来的初始化方式仍然可以使用,不过新方式将会带来以下优点:初始化与应用原先业务代码完全隔离,使得原先真正的Application可以修复,并且减少了补丁预加载时间。而且,新方式已经优先支持Android 8.0版本。
本文使用这种新型方式。

1- 导入SophixStubApplication
需要加入这个类:

package com.my.pkg;
import android.app.Application;
import android.content.Context;
import android.support.annotation.Keep;
import android.util.Log;
import com.taobao.sophix.PatchStatus;
import com.taobao.sophix.SophixApplication;
import com.taobao.sophix.SophixEntry;
import com.taobao.sophix.SophixManager;
import com.taobao.sophix.listener.PatchLoadStatusListener;
import com.my.pkg.MyRealApplication;
/**
 * Sophix入口类,专门用于初始化Sophix,不应包含任何业务逻辑。
 * 此类必须继承自SophixApplication,onCreate方法不需要实现。
 * AndroidManifest中设置application为此类,而SophixEntry中设为原先Application类。
 * 注意原先Application里不需要再重复初始化Sophix,并且需要避免混淆原先Application类。
 * 如有其它自定义改造,请咨询官方后妥善处理。
 */
public class SophixStubApplication extends SophixApplication {
    private final String TAG = "SophixStubApplication";
    // 此处SophixEntry应指定真正的Application,也就是你的应用中原有的主Application,并且保证RealApplicationStub类名不被混淆。
    @Keep
    @SophixEntry(MyRealApplication.class)
    static class RealApplicationStub {}
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //         如果需要使用MultiDex,需要在此处调用。
        //         MultiDex.install(this);
        initSophix();
    }
    private void initSophix() {
        String appVersion = "0.0.0";
        try {
          appVersion = this.getPackageManager()
                         .getPackageInfo(this.getPackageName(), 0)
                         .versionName;
        } catch (Exception e) {
        }
        final SophixManager instance = SophixManager.getInstance();
    instance.setContext(this)
            .setAppVersion(appVersion)
            .setSecretMetaData(null, null, null) //三个参数分别对应AndroidManifest里面的AppId、AppSecret、RSA密钥,可以不在AndroidManifest设置而是用此函数来设置Secret。放到代码里面进行设置可以自定义混淆代码,更加安全,此函数的设置会覆盖AndroidManifest里面的设置,如果对应的值设为null,默认会在使用AndroidManifest里面的。
            .setEnableDebug(true)//默认为false,设为true即调试模式下会输出日志以及不进行补丁签名校验. 线下调试此参数可以设置为true, 它会强制不对补丁进行签名校验, 所有就算补丁未签名或者签名失败也发现可以加载成功. 但是正式发布该参数必须为false, false会对补丁做签名校验, 否则就可能存在安全漏洞风险。
            .setEnableFullLog()
            .setPatchLoadStatusStub(new PatchLoadStatusListener() {
                @Override
                public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
                    if (code == PatchStatus.CODE_LOAD_SUCCESS) {
                        Log.i(TAG, "sophix load patch success!");
                    } else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
                        // 如果需要在后台重启,建议此处用SharePreference保存状态。
                        Log.i(TAG, "sophix preload patch success. restart app to make effect.");
                        /** 不可以直接Process.killProcess(Process.myPid())来杀进程,这样会扰乱Sophix的内部状态。
                         * 因此如果需要杀死进程,建议使用这个方法,它在内部做一些适当处理后才杀死本进程。*/
                        instance.killProcessSafely();
                    }
                }
            }).initialize();
    }
    @Override
    public void onCreate() {
      super.onCreate();
      // queryAndLoadNewPatch不可放在attachBaseContext 中,否则无网络权限,建议放在后面任意时刻,如onCreate中
      SophixManager.getInstance().queryAndLoadNewPatch();
      /** 补丁在后台发布之后, 并不会主动下行推送到客户端, 客户端通过调用queryAndLoadNewPatch方法查询后台补丁是否可用*/
    }
}

初始化sophix务必放在attachBaseContext中,onCreate不需要自行实现。同时自定义的SophixStubApplication需要继承com.taobao.sophix.SophixApplication。
这其中,关键一点是:

@Keep
@SophixEntry(MyRealApplication.class)
static class RealApplicationStub {}

SophixEntry应指定项目中原先真正的Application(原项目里application的android::name指定的),这里用MyRealApplication指代。并且保证RealApplicationStub类名不被混淆。而SophixStubApplication的类名和包名可以自行取名。

这里的Keep是android.support包中的类,目的是为了防止这个内部静态类的类名被混淆,因为sophix内部会反射获取这个类的SophixEntry。如果项目中没有依赖android.support的话,就需要在progurad里面手动指定RealApplicationStub不被混淆。

2- 然后,在proguard文件里面需要加上下面内容:

-keepclassmembers class com.my.pkg.MyRealApplication {
  public <init>();
}
# 如果不使用android.support.annotation.Keep则需加上此行
# -keep class com.my.pkg.SophixStubApplication$RealApplicationStub

目的是防止真正Application的构造方法被proguard混淆。

最后,需要把AndroidManifest里面的application改为这个新增的SophixStubApplication类:

 <application
    android:name="com.my.pkg.SophixStubApplication"
    ... ...>
    ... ...

sample源码


生成、上传、调试补丁

下载打包工具:
patch补丁包生成需要使用到打补丁工具SophixPatchTool, 如还未下载打包工具,请前往下载Android打包工具。

该工具提供了Windows和macOS和Linux版本,Windows下运行SophixPatchTool.exe,macOS下运行SophixPatchTool.app,Linux下(Ubuntu 16.04 64bit最佳)运行SophixPatchTool。并且需要安装Java环境且在JDK7或以上才能正常使用。

我是先 生成调试包,有问题的程序,Build apk,改名字为旧包.apk。然后修复完,再Build apk,改名字为新包.apk。这样能看Log。测试成功后,再生成发布包,再测试一遍。

补丁打包工具主对话框

点击【高级】,弹出 补丁和签名设置


image.png

点击【设置】


image.png

Ps:

mac下的补丁工具若出现一打开就崩溃的情况,请将补丁工具移到“应用程序”目录下即可。

点击 Go ,生成的补丁如下图:


补丁

补丁文件名必须为:sophix-patch.jar。不能更改。

上传补丁

点击 【上传补丁】,补丁版本列表更新。

上图有个二维码,在正式发布前,我们用测试工具扫码测试下。
测试工具是个apk。它是通过扫描补丁二维码,下载到手机上,然后通过在apk界面上输入你要测试的应用包名,将补丁打到应用里。

调试补丁

调试工具App地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/hotfix_debug_tool-release.apk

调试工具App界面

那个截图上的 【断开连接应用】,最开始是 【连接应用】

看到调试App界面是输出信息,有以下几条,就代表成功了。
app connect successful.
patch download success.
please restart app to reload new patch as exist old patch.

打开你的bug应用,就可以看到变化了。
来个截图示例,应用源码就是文章前面给的sample


sophix调试补丁.gif

SophixTest原来只显示个 helloworld,经过Sophix调试工具V3的打补丁后,再次打开SophixTest就变成了有福利字样,并显示张美女图片。


补丁灰度发布、全量发布、机型过滤

注意事项:

应用版本详情页

灰度发布

在应用版本详情页,点击补丁版本列表里的【查看详情】,进入 补丁详情页。


补丁详情页

在刚刚上传完补丁后,补丁处于 等待中 的状态,勾选 灰度发布

设置完设备数,客户端拉取补丁会消耗该设备数,达到灰度设备数后,灰度补丁自动置为停止状态。
设备数:指设备请求更新该补丁的次数,并不等于绝对设备数。

例如:1个设备请求了2次更新该补丁,则会消耗掉2的设备数。

这时,当用户打开客户端,就会拉取线上的补丁,修复程序。
还记得代码中的queryAndLoadNewPatch()方法吗,它的作用去看sample源码注释。

注:

· 只会下载补丁版本号比当前应用存在的补丁版本号高的补丁, 比如当前应用已经下载了补丁版本号为5的补丁, 那么只有后台发布的补丁版本号>5才会重新下载.

· 在上传新的补丁之后,要调试时,如果以往的补丁有处于 已灰度已发布状态,要停止发布。 如果不停止,最新的补丁处于等待中,也就是未发布。那么当你打开客户端,它会拉取以往发布的补丁修复程序,这样会影响你观测调试结果。

· 后台数据可能有少许延迟。

界面变成:


停止发布 后

如果当前版本在停止前处于灰度中,继续发布可以:

· 重设灰度发布规则,新的规则中设备数必须大于之前的值。
· 改为全量发布。

灰度状态下继续发布

所以,从灰度发布到全量发布的步骤是

· 先在补丁详情页勾选灰度发布,点击确认发布
· 推送完所有灰度设备后,点击停止发布
· 再点击继续发布,弹出框里选择全量发布

如果当前版本在停止前处于全量发布,继续发布可以:

继续全量发布。 --- 对,你没看错,就是逗你玩!

使用回滚功能必需要具备一下几个条件:

· 当前的版本已停止发布。
· 该版本之前存在至少一个全量发布的历史版本。

全量发布

选择全量发布后,将对所有安装了当前应用版本(即之前创建应用时所填写的应用版本号)的设备推送该补丁。

与灰度发布类似,在全量发布会可以根据自身需要停止本次全量发布,停止发布后可以选择:

· 继续全量发布。
· 回滚版本(如果存在历史版本)

添加过滤机型

全量发布后,我们可以添加过滤机型。
不全量发布是不可以添加机型过滤的

image.png
在App版本详情页,点击【添加过滤机型】
点击添加过滤机型弹出框
这里对过滤机型的弹出框参数进行说明:

在控制台中,有相应的系统版本列表可供选择。如果列表中没有需要自定义,请按如下标准获取系统版本。

android.os.Build.VERSION.RELEASE
例如系统版本结果是:7.1

在控制台中,我们有相应的品牌列表供选择使用。如果需要自定义,请按如下标准获取手机品牌,注意实际过滤时不区分大小写。

android.os.Build.BRAND
例如手机品牌是:Xiaomi

目前由于手机机型庞杂,没有提供选择列表供选择,后续会支持。填写手机机型时请按如下标准,不区分大小写。

android.os.Build.MODEL
例如手机型号是:OPPO R11

【注意】如果想设置全部机型,请在自定义机型里面,输入 :all
(就是 冒号+all)

到这里,sophix集成的全部内容就结束了。阿里热修复官方的文档有点琐碎,我把重点和注意点都挑出来了。读完这四篇,相信你会迅速集成sophix到自己的应用里。

这再给出官方接入文档地址,给还想看官方文档的朋友。官方接入文档

上一篇下一篇

猜你喜欢

热点阅读