阿里云HotFix热修复使用笔记

阿里云HotFix热修复使用笔记

今天阿里云出了一本书《深入探索Android热修复技术原理》,下载下来了还没有看,准备找个时间好好看看,然后又听说阿里云又推出了一个热修复框架。下面我们来看看这个热修复框架和现在其他流行的热修复框架有什么区别。

平台 阿里云移动热修复 Qzone AndFix Robust Tinker
即时生效 yes no yes yes no
性能损耗 较小 较大 较小 较小 较大
侵入式打包 无侵入式打包 依赖侵入式打包 无侵入式打包 依赖侵入式打包 依赖侵入式打包
Rom体积 较小 较小 较小 较小 较小
接入复杂度 傻瓜式接入 比较简单 比较简单 复杂 复杂
补丁包大小 较小 较大 较小 一般 较小
全平台支持 yes yes yes yes yes
类替换 yes yes yes no yes
so替换 yes no no no yes
资源替换 yes yes no no yes

总体来说

  1. 补丁即时生效,不需要应用重启;
  2. 补丁包同样采用差量技术,生成的PATCH体积小;
  3. 对应用无侵入,几乎无性能损耗;
  4. 傻瓜式接入。

下面我们开始看看怎么来实现热修复。

创建应用

首先要有阿里云的账号,经过实名验证才可以去创建应用

  1. 登录移动热修复控制台:https://hotfix.console.aliyun.com/
    ,点击右上角创建App,跳转到2 MHub App管理控制台,或者直接访问MHub控制台:https://mhub.console.aliyun.com/
  1. 在MHub控制台点击右上角创建App,填入App名称、分类和描述。

这里要注意这两个控制台一个是创建App时候用的一个是,发布版本的时候用的,刚开始没注意怎么也找不到发布版本的入口,还去提了一个工单,好尴尬…

这是图片啊

Android Studio接入

gradle远程仓库依赖

打开项目找到app的build.gradle文件,添加如下配置:

添加maven仓库地址:

1
2
3
4
5
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}

添加gradle坐标版本依赖:

1
compile 'com.aliyun.ams:alicloud-android-hotfix:3.0.3'

如果集成了其他阿里系的sdk导致utdid冲突,则如下处理,关闭依赖传递性

1
2
3
compile ('com.aliyun.ams:alicloud-android-hotfix:3.0.3') {
exclude(module:'alicloud-android-utdid')
}

权限声明

android6.0注意动态权限申请

1
2
3
4
5
6
<! -- 网络权限 -->
<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"/>

配置AndroidManifest文件

AndroidManifest.xml中间的application节点下添加如下配置:

1
2
3
4
5
6
7
8
9
10
<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密钥" />

混淆配置

1
2
3
4
5
6
7
8
9
10
#基线包使用,生成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

SDK接口使用说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// initialize最好放在attachBaseContext最前面
SophixManager.getInstance().setContext(this)
.setAppVersion(appVersion)
.setAesKey(null)
.setEnableDebug(true)
.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) {
// 表明补丁加载成功
} else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
// 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;
// 建议: 用户可以监听进入后台事件, 然后应用自杀
} else if (code == PatchStatus.CODE_LOAD_FAIL) {
// 内部引擎异常, 推荐此时清空本地补丁, 防止失败补丁重复加载
// SophixManager.getInstance().cleanPatches();
} else {
// 其它错误信息, 查看PatchStatus类说明
}
}
}).initialize();
// queryAndLoadNewPatch不可放在attachBaseContext 中,否则无网络权限,建议放在后面任意时刻,如onCreate中
SophixManager.getInstance().queryAndLoadNewPatch();

注意上面注释的地方有一个queryAndLoadNewPatch不能在attachBaseContext中,
queryAndLoadNewPatch这个方法我们可以在初始化之后随时调用,这个方法放在哪就可以在哪里查询后台是否有最新的补丁并下载。

打差异包

阿里云提供一个带有可视化界面的打包工具

使用简单只需一个新包一个旧包,然后点一下go按钮就可以了

然后在阿里云平台上发布版本,如果需要本地测试的话,阿里云也提供一个测试工具,使用起来也特别简单
,其他具体的sdk就看文档就可以了。

注意(敲黑板!!!)

有一个问题,有的时候能热修复不用重启,有的时候冷修复需要重启,那究竟何时走即使生效热修复,何时走冷启动修复?

这是根据代码变动情况决定的。

代码若变动小,一般会走即时生效热修复,而如果代码变动大,比如在已存在的类中新增方法,修改了so等情况,都会走冷启动。这是由打包工具自行检测代码变化来判断的,开发者无需考虑。

并且,在运行期如果在所运行的设备上检测到不支持即时生效热修复,也会在该机型上走强制冷启动修复。

而如果对稳定性有极高要求,可开启强制冷启动选项(在打包工具中高级中可以选择强制冷启动),这样任何变动都会按强制冷启动方式打包。

如果想补丁下载好了的时候自动修复,可以在另一个进程开启一个Service不要和app在同一个进程,这个serviec的任务就是启动一个app然后自己干掉自己,在application里面回掉的的方法里判断CODE然后选择重启app,但是最好不要这么干,毕竟强制重启体验也不太友好。

1
2
<service android:name=".killSelfService"
android:process=":remote666"/>

下面是自杀服务和一个重启工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class killSelfService extends Service {
private static long stopDelayed=2000;
private Handler handler;
private String PackageName;
public killSelfService() {
handler=new Handler();
}
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
stopDelayed=intent.getLongExtra("Delayed",1000);
PackageName=intent.getStringExtra("PackageName");
handler.postDelayed(new Runnable() {
@Override
public void run() {
//开启应用然后自杀
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage(PackageName);
startActivity(LaunchIntent);
killSelfService.this.stopSelf();
}
},stopDelayed);
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RestartAPPTool {
public static void restartAPP(Context context, long Delayed){
//开启服务传递包名和开启延迟
Intent intent1=new Intent(context,killSelfService.class);
intent1.putExtra("PackageName",context.getPackageName());
intent1.putExtra("Delayed",Delayed);
context.startService(intent1);
android.os.Process.killProcess(android.os.Process.myPid());
}
public static void restartAPP(Context context){
restartAPP(context,2000);
}
}

现在这个框架属于公测版,暂时免费,但是以后会收费。