Android 热修复之阿里的Sophix
一:简介
移动热修复(Mobile Hotfix)是面向Android、iOS平台应用提供的在线热修复服务方案,产品基于阿里巴巴首创的Hotpatch技术,提供细粒度热修复能力,无需等待发版即可实时修复应用线上问题,用户全程无感知。
二:Android SDK稳健接入
第一步:在项目下的build.gradle下添加
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }//Maven仓库地址
}
}
额外话题:
在rootProject下的build.gradle中:buildscript的repositories和allprojects的repositories有什么区别?
1、 buildscript里是gradle脚本执行所需依赖,分别是对应的maven库和插件
2、 allprojects里是项目本身需要的依赖,比如代码中某个类是打包到maven私有库中的,那么在allprojects—>repositories中需要配置maven私有库,而不是buildscript中,不然找不到。
第二步:在app下build.gradle添加热修复的依赖
android {
......
defaultConfig {
applicationId "com.xxx.xxx" //包名
ndk {
//选择要添加的对应cpu类型的.so库。热修复支持5种
abiFilters 'arm64-v8a','armeabi', 'armeabi-v7a', 'x86_64','x86'
}
}
}
dependencies {
implementation 'com.aliyun.ams:alicloud-android-hotfix:3.2.18'
}
第三步添加应用权限Androidmanifest.xml
//允许网络请求,下载补丁时使用。
<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" />
//外部存储读权限,调试工具从SD卡加载本地补丁需要。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
第四步:除了下面配置文文件引入App ID等,下面这种方式直接引入也可以
<application
android:name="com.alibaba.sophix.demo.SophixStubApplication"
android:allowBackup="true"
android:icon="@mipmap/app_logo"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="App ID" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="App Secret" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="RSA密钥" />
第四步:下载我们在平台上添加的项目的生成的配置文件
为方便EMAS各产品SDK快速、轻量接入,EMAS提供了统一的SDK依赖方式,供各产品SDK添加依赖使用。用户使用统一接入后,无需手动管理各产品SDK依赖,由插件自行接管完成读取和配置。
并将json文件放到应用根目录下(/app/)。Android端配置文件:aliyun-emas-services.json其内容如下:
配置文件:
{
"config": {
"emas.appKey":"xxxxx",
"emas.appSecret":"xxxxxx",
"emas.packageName":"com.xxx.xxx.demo",
"hotfix.idSecret":"xxxxx",
"hotfix.rsaSecret":"xxxxx",
"httpdns.accountId":"xxxxx",
"httpdns.secretKey":"xxxxxxxxx"
"appmonitor.tlog.rsaSecret":"xxxxxxxxxx",
"appmonitor.rsaSecret":"xxxxxxxxxx"
},
"services": {
"hotfix_service":{//移动热修复,status=1,表示使用对应产品,status=0不使用
"status":1,
"version":"3.2.14"
},
"ha-adapter_service":{//性能分析/远程日志的基础库
"status":1,
"version":"1.1.3.4-open"
},
"feedback_service":{//移动用户反馈
"status":1,
"version":"3.3.1"
},
"tlog_service":{//远程日志
"status":1,
"version":"1.1.2.3-open"
},
"httpdns_service":{
"status":1,
"version":"1.3.2.3"
},
"apm_service":{//性能分析
"status":1,
"version":"1.0.7.9-open"
},
"man_service":{//移动数据分析
"status":1,
"version":"1.2.4"
},
"cps_service":{//移动推送
"status":1,
"version":"3.1.12"
}
},
"use_maven":true,
"proguard_keeplist":"\n#httpdns\n-keep class com.taobao.** {*;}\n-keep class com.alibaba.** {*;}\n-keep class com.ta.**{*;}\n-keep class com.ut.**{*;}\n-dontwarn com.taobao.**\n-dontwarn com.alibaba.**\n-dontwarn com.ta.**\n-dontwarn com.ut.**\n\n#cps\n-keep class com.taobao.** {*;}\n-keep class com.alibaba.** {*;}\n-keep class com.ta.**{*;}\n-keep class com.ut.**{*;}\n-dontwarn com.taobao.**\n-dontwarn com.alibaba.**\n-dontwarn com.ta.**\n-dontwarn com.ut.**\n-keepclasseswithmembernames class ** {\nnative <methods>;\n}\n-keepattributes Signature\n-keep class sun.misc.Unsafe { *; }\n-keep class com.alipay.** {*;}\n-dontwarn com.alipay.**\n-keep class anet.**{*;}\n-keep class org.android.spdy.**{*;}\n-keep class org.android.agoo.**{*;}\n-dontwarn anet.**\n-dontwarn org.android.spdy.**\n-dontwarn org.android.agoo.**\n\n#hotfix\n#基线包使用,生成mapping.txt\n-printmapping mapping.txt\n#生成的mapping.txt在app/buidl/outputs/mapping/release路径下,移动到/app路径下\n#修复后的项目使用,保证混淆结果一致\n#-applymapping mapping.txt\n#hotfix\n-keep class com.taobao.sophix.**{*;}\n-keep class com.ta.utdid2.device.**{*;}\n#防止inline\n-dontoptimize\n\n#man\n-keep class com.taobao.** {*;}\n-keep class com.alibaba.** {*;}\n-keep class com.ta.**{*;}\n-keep class com.ut.**{*;}\n-dontwarn com.taobao.**\n-dontwarn com.alibaba.**\n-dontwarn com.ta.**\n-dontwarn com.ut.**\n\n#feedback\n-keep class com.taobao.** {*;}\n-keep class com.alibaba.** {*;}\n-keep class com.ta.**{*;}\n-keep class com.ut.**{*;}\n-dontwarn com.taobao.**\n-dontwarn com.alibaba.**\n-dontwarn com.ta.**\n-dontwarn com.ut.**\n"
}
配置文件引用
在项目级目录下build.gradle(/build.gradle)文件中添加阿里云Maven仓库地址和emas-services插件。
buildscript {
repositories {
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
dependencies {
// 添加emas-services插件
classpath 'com.aliyun.ams:emas-services:1.0.1'//引入
}
}
allprojects {
repositories {
...
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
}
在应用级build.gradle(<project>/<app-module>/build.gradle)文件中添加代码段,应用插件。
apply plugin: 'com.aliyun.ams.emas-services'
第五步:初始化
初始化的调用应该尽可能的早,必须在Application.attachBaseContext()的最开始(在super.attachBaseContext之后,如果有Multidex,也需要在Multidex.install之后)进行SDK初始化操作,初始化之前不能用到其他自定义类,否则极有可能导致崩溃。而查询服务器是否有可用补丁的操作可以在后面的任意地方。不建议在Application.onCreate()中初始化,因为如果带有ContentProvider,就会使得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(MyApplication .class)
static class RealApplicationStub {}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 如果需要使用MultiDex,需要在此处调用。
// MultiDex.install(this);
initSophix();
}
private void initSophix() {
String appVersion = "1.1.1";//app对应的版本名
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();
}
}
其中最重要的是
@Keep
@SophixEntry(MyApplication .class)
static class RealApplicationStub {}
//SophixEntry应指定项目中原先真正的Application(原项目里application的android::name指定的)
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SophixManager.getInstance().queryAndLoadNewPatch();
/// queryAndLoadNewPatch为拉取控制台补丁
//不可放在attachBaseContext 中,否则无网络权限,建议放在主进程任意时刻,如Application的onCreate中
//SophixManager.getInstance().queryAndLoadNewPatch();这个方法用来查询有没有补丁包生成
}
}
//现在的application
<application
android:name=".SophixStubApplication"
android:allowBackup="false"/>
第六步:混淆配置
#基线包使用,生成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
阿里热修复接入文档
https://help.aliyun.com/docum...
END:当你真正喜欢做一件事时,自律就会成为你的本能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。