第一章、准备工作
1、申请阿里云账号;
2、开通阿里云移动管理服务;
3、开通阿里云移动服务平台EMAS.
4、创建一个新产品,即需要接入的APP产品。
第二章:接入热修复SDK
您可以在应用管理页面获取配置SDK所需要的基本信息。包括:
- AppKey:用于AppKey是阿里云上应用的唯一标识,热修复的appId可设置AppKey。
- RSA密钥: RSA密钥是保存在客户端本地用于解密patch包过程中使用的解密密钥。(推荐使用chrome浏览器下载)
- AppSecret:用于URL请求时生成合法验签标识的key。
Demo程序下载地址:
https://github.com/aliyun/alicloud-android-demo
安卓Demon:
https://github.com/joedan0104/SophixDemo
1、添加热修复依赖库
在项目的app的build.gradle中添加Maven仓库
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
在dependencies增加热修复库坐标版本依赖
compile 'com.aliyun.ams:alicloud-android-hotfix:3.2.8'
2、AndroidManifest增加需要的权限
包括读写SD卡、网络权限。
<! -- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<! -- 外部存储读权限,调试工具加载本地补丁需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
3、配置APP热修复相关数据
在AndroidManifest的application节点下,配置热修复字段。
<!-- 热修复字段 -->
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="${HOTFIX_APPID}" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="${HOTFIX_APPSECRET}" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="${HOTFIX_RSA}" />
这些字段在编译的时候写入具体的值。
这几个值的阿里云控制台用户创建的APP对应的热修复的值。
其中APPID:对应的AppKey
APPSECRET:对应的AppSecret
RSA:对应的Rsa
def config = rootProject.ext// android基本配置
android {
compileSdkVersion config.android.compileSdkVersion
buildToolsVersion config.android.buildToolsVersion
defaultConfig {
applicationId "com.cdel.sophixdemo"
minSdkVersion config.android.minSdkVersion
targetSdkVersion config.android.targetSdkVersion
versionCode 1
versionName "1.0.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// dex突破65535的限制
multiDexEnabled true
//个别机器添加热修复导致Vitamio 崩溃问题
ndk {
abiFilters "armeabi", "armeabi-v7a"
}
// AndroidManifest替换参数
manifestPlaceholders = [
HOTFIX_APPID : "25994860",
HOTFIX_APPSECRET : "f63fcee692a46eb81da4c7a10fe1cd4f",
HOTFIX_RSA : "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCiD+oeBqhp79WsNwHUjzUy6ljWM6LgATtXoSFm0KErs8DeF3BUPx9P0fSF8grXh7/OoLlAk5uLC11v51mSbKP9OhQ67doHFBhJT8Og5ixIaUT9vV05KksxM85cXV/Ze7o9rjzLtHKbg4ySe/LjiQNdp7SLAhauUXHrEMJpGJaORFVQVtqCrkMjgsvx1ZPmhI8mg6U6VOYcVD2rkLV1jyQcSvcJXtJtERtRBLK2R3IRiC2Oy/IosYJjGeOrN/KMWbMQOC36p7rMOj+xYeOiDyxHX9v+5TRUZ/NXjku57CmjIVcBfF7goBvtay1TIsp1ggDp51Wsg/fGB5KIT6ZPdhzhAgMBAAECggEAYVc1w/szxQ2tRdd0Xfv2dDKWUXKKbgnPXv8FVQATDnDVc5CYg8G2SPdO6/VJ1dNPvgiDvOnsD0QwRzqqbmyaw0rTQ+IBhybmv4INeuNFzeiM6+uFxwRETbVUSd476CGM5+dz1Nb5z7bgBEw2w7xULTjCFIyYmvOIfSx/2ShPpfsiRES9fL3LOau5GqFeHczjwr0nFVuAALB4xnS8cw5z9dZfawcJUOCQKTYzUpSlLvetEAYRDXdl66mPQVdG0VLtKuyxZfIFNyyifY7IaClPvZt1ZZXgr+rKRj+m+vbVU6r5eHnQTqC4m5AVRtCA97SXYPTFFR1QH8a7YWki0/sTUQKBgQD/xnpD+mZfxdexEMlVfBIjrVfcEBeky2hVNgCXDqd2oe/NKQC3vbcgDV9NslhlBTIdXvHvvLUHSSoBlK8lic/53VYUbLQtyEmEM7k5gV6zJBLudyfXgMd30Jyq6Y4w6J4UKOCy/vxOu8DVbMBG56XpnjvyoEVBCSuJXPDXSnUE2wKBgQCiNFyC9eF/r2j12K5vERIUzGH11w6T7yIcY66JKce2m1oqucyo7ZBX3HXxOUMM6676QmesMJF2/01vZrEe8usg0zFpyhKXLc9sk8nEYd5Ip9Gui2LmAsRXTzXNR7tVI4Bxueqrb7GKrOMteplMy0sze/nOAOnZbszSxiXjqsbT8wKBgDC52e77U+do5EdKIGDVqwr66eL1edueGHkrOX+Nfh+eh/V4mzdlv+4uPfqqQCa2j66CuOpn88C4knUMozUuhN1f6hcoAkc6ga7av62R1L2h4K1nK8LlOJq9tirlJX5xwjOq+et/ogdJw1mlRxf652OTDm6RU8ApBA8+Em+hMpvDAoGAZoZSMzTXY62r2jkriGhx1VOaV5mnTTpJhUg8edY+td1cEMV/5wbBxcdDhUV8bB5Ma0Kt5NiOGXklqNRv/+/rTsQu538iYQxDn42KauaxaZspnfnceSYmlEks6KP+dMIL3dZ1zga7YbwFnClx3GhjEBe8vck2BolSTaeRK2Y2ZqECgYAHBocOQA8zhUhTMaA/yPM1gP6V+bQu92wLf318OpXRMr6/DNKnoujhFCB3wsemBWrJCdpIZifdOaNQnw6ZIyPiK88q9yA8PYUBeF+KjX0mk8k8/vLoUUJqe2bf7WV+3M0jxjlBhWpeN8rFSbuMN1gEvX1dRKIs8yNbY9FBePejTw==",
]
}
4、创建Sophix入口程序
/**
* Sophix入口类,专门用于初始化Sophix,不应包含任何业务逻辑。
* 此类必须继承自SophixApplication,onCreate方法不需要实现。
* 此类不应与项目中的其他类有任何互相调用的逻辑,必须完全做到隔离。
* AndroidManifest中设置application为此类,而SophixEntry中设为原先Application类。
* 注意原先Application里不需要再重复初始化Sophix,并且需要避免混淆原先Application类。
* 如有其它自定义改造,请咨询官方后妥善处理。
*/
public class SophixStubApplication extends SophixApplication {
private final String TAG = "SophixStubApplication";
// 此处SophixEntry应指定真正的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)
.setEnableDebug(true)
.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.");
}
}
}).initialize();
}
}
注意:SophixEntry应指定项目中原先真正的Application(原项目里application的android::name指定的),这里用MyRealApplication指代。并且保证RealApplicationStub类名不被混淆。而SophixStubApplication的类名和包名可以自行取名。
@Keep
@SophixEntry(MyRealApplication.class)
static class RealApplicationStub {}
这里的Keep是android.support包中的类,目的是为了防止这个内部静态类的类名被混淆,因为sophix内部会反射获取这个类的SophixEntry。如果项目中没有依赖android.support的话,就需要在progurad里面手动指定RealApplicationStub不被混淆,详见下文。
把AndroidManifest里面的application改为这个新增的SophixStubApplication类:
<application
android:name=".SophixStubApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
这样便完成了新方式的初始化接入改造。
总结一下,过程一共有四个步骤:
- 把此SophixStubApplication入口类添加进项目中,所有Sophix相关初始化放在此类中。并且不应包含开发者的任何业务逻辑代码。 若使用了MultiDex,也应在SophixStubApplication的initSophix之前添加,并且需要记得在原来的Application里面去除MultiDex,避免重复调用导致问题。
- 把RealApplicationStub的SophixEntry注解的内容改为自己原先真正的MyRealApplication类。
- 混淆文件中确保某些内容不被混淆。
- AndroidManifest里面的application改为新增的SophixStubApplication入口类。
5、配置热修复混淆
混淆配置文件proguard-rules.pro增加热修复相关配置
#基线包使用,生成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.**{*;}
-dontwarn com.alibaba.sdk.android.utils.**
#防止inline
-dontoptimize
-keepclassmembers class com.cdel.sophixdemo.ModelApplication {
public <init>();
}
# 如果不使用android.support.annotation.Keep则需加上此行
# -keep class com.my.pkg.SophixStubApplication$RealApplicationStub
注意:
*生成基线包,使用
-printmapping mapping.txt
*生成补丁后的包,使用
-applymapping mapping.txt
然后把基线包的mapping文件拷贝到app下边。
两者不能同时出现。
6、查询和下载热修复补丁
// queryAndLoadNewPatch不可放在attachBaseContext 中,否则无网络权限,建议放在后面任意时刻,如onCreate中
SophixManager.getInstance().queryAndLoadNewPatch();
热修复补丁下载完成以后,等APP完全退出,下载APP启动的时候自动安装。
第三章 生成补丁包
生成补丁包的过程。
1)生成基线包
混淆文件中配置
*生成基线包,使用
-printmapping mapping.txt
打包生成基线包。
2)生成修复包
混淆文件中配置
*生成补丁后的包,使用
-applymapping mapping.txt
把基线包的mapping文件复制到app目录下。打包生成修复包。
3)生成补丁包
下载安装补丁环境
3.1.1 下载打包工具
patch补丁包生成需要使用到打补丁工具SophixPatchTool, 如还未下载打包工具,请前往下载Android打包工具。
-
Mac版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_macos.zip
-
Windows版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_windows.zip
-
Linux版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_linux.zip
-
调试工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/hotfix_debug_tool-release.apk
该工具提供了Windows和macOS和Linux版本,Windows下运行SophixPatchTool.exe,macOS下运行SophixPatchTool.app,Linux下(Ubuntu 16.04 64bit最佳)运行SophixPatchTool。并且需要安装Java环境且在JDK7或以上才能正常使用。
3.1.2 生成Patch
3.1.2.1 主对话框
- 旧包:<必填> 选择基线包路径(有问题的APK)。
- 新包:<必填> 选择新包路径(修复过该问题APK)。
- 日志:打开日志输出窗口。
- 高级:展开高级选项,见1.2.2。
- 设置:配置其他信息。
- GO!:开始生成补丁。
3.1.2.3 设置对话框
- 补丁输出路径:<必填> 指定生成补丁之后补丁的存放位置,必须是已存在的目录。
- Key Store Path:<选填>本地的签名文件的路径,不输入则不做签名。如果基线包是签名包,此处需要选择keystore文件。如果用户的keystore文件显示不出来,可以将原来keystore的文件拷贝一份,重命名为keystore.xxx,这样就可以显示出来。
- Key Store Password:<选填>证书文件的密码。Key Store Path设置时必填
- Key Alias:<选填>Key的别名。Key Store Path设置时必填
- Key Passwrod:<选填>Key的密码。Key Store Path设置时必填
- AES Key:<选填>自定义aes秘钥, 必须是16位数字或字母的组合。必须与setAesKey中设置的秘钥一致。可不填。
- Filter Class File:<选填>本地的白名单类列表文件的路径,放进去的类不会再计算patch,文件格式: 一行一个类名。
3.1.2.3 生成补丁包
点击GO 按钮,生成补丁包。
sophix-patch就是生成的补丁包。
第四章 发布补丁包
发布补丁
HotFix提供了多种发布方式,方便您根据自身业务需要选择性使用。
- 上传补丁后,会展示补丁列表信息,点击“详情”,进入发布页面
- 进入详情页面,点击“新建发布”,进入发布流程
1.1 本地测试
HotFix提供了调试工具实现本地测试,方便您在正式发布前,在您的手机本地进行测试。
步骤如下:
- 下载安装HotFix 调试工具。
- 使用HotFix 调试工具获取补丁;
- Android调试工具使用说明。
1.2 灰度发布
HotFix提供灰度发布模式,您可以在控制台发布灰度批次,并为该批次指定灰度人数
和标签
,客户端拉取到补丁时会消耗该灰度人数,达到指定数量后,灰度批次自动置为停止状态。
1.2.1 发布人数
此项为设置最多可供设备请求到该批次更新补丁的次数。该计数以请求到更新补丁的次数为准,如:
1个设备请求到更新补丁之后,删除本地缓存再次请求成功1次,则总共会消耗掉2个次数。
1.2.2 指定标签
注:Android SDK从 3.2.7 版本开始支持设置标签,若接入的SDK低于该版本,新建灰度批次时忽略此项即可。
此项为设置灰度附带的标签条件。只有设置了对应标签的设备,才能请求到本批次的更新补丁。如:
端上设置标签为["tag1"], 发布时指定标签为["tag1"], 能拉取到;
端上设置标签为["tag1","tag2"], 发布时指定标签为["tag1"], 能拉取到;
端上设置标签为["tag1"], 发布时指定标签为["tag1","tag2"], 能拉取到;
端上设置标签为["tag1","tag3"], 发布时指定标签为["tag1", "tag3"], 能拉取到;
可以看到,只要端上设置的标签[组],和发布灰度批次时指定的标签[组],交集不为空,就能拉取到更新补丁。
特别注意:
1. 输入标签后,按回车,该标签输入才算成功;您可以通过多次输入,多次回车,来实现输入标签组;
2. 不输入任何标签,则该批次不会对端上携带的标签做任何校验;
1.2.3 后置操作
发布灰度批次后可以根据实际需要停止灰度批次,停止后可以选择:
- 发布新的灰度批次;
- 发布新的全量批次;
- 回滚版本(如果存在历史版本)见
2.9 发布回滚
。
1.3 全量发布
HotFix提供全量发布模式,选择全量发布后,将对所有安装了当前应用版本(即之前创建应用时所填写的应用版本号)的设备推送该补丁。
与灰度发布类似,在全量发布会可以根据自身需要停止本次全量发布,停止发布后可以选择:
- 继续全量发布。
- 回滚版本(如果存在历史已经发布过全量批次的版本),见
2.9 发布回滚
。
1.4 停止发布
HotFix提供停止发布功能,用户停止某个发布批次后,系统将停止该批次补丁的继续发布,但已加载该补丁的设备会依然保持安装该补丁的状态。
1.5 发布回滚
HotFix提供发布回滚功能,用户选择回滚的目标补丁后,确认回滚后,所有该应用版本下设备都会获取到该目标补丁,从而实现回滚。
使用回滚功能必需要具备以下条件:
- 该版本之前存在至少一个
全量发布
的历史版本。