阿里热修复之Sophix
集成三步曲
一、注册账号&创建热修复应用
下面我们将创建热修复测试应用
下面是我们创建的应用中的参数说明和使用
二、代码集成
1、首先就是在主module下的build.gradle下添加maven仓库地址:
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
我使用中在主module下的build.gradle下的完整代码如下:
buildscript {
repositories {
maven { url 'https://maven.google.com' }
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
2、其次,在app的build.gradle中引入依赖包:
dependencies {
compile 'com.aliyun.ams:alicloud-android-hotfix:3.0.2'
}
当然了,你项目中可能还需要引入其他依赖包或者加入签名什么的,这些都正常引入即可,下面是我的demo中app下的build.gradle中的代码,如下:
apply plugin: 'com.android.application'
android {
signingConfigs {
release {
keyAlias 'rec'
keyPassword '123456'
storeFile file('rec.jks')
storePassword '123456'
}
}
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.dota.recyclerviewdemo"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:design:25.3.1'
compile('com.aliyun.ams:alicloud-android-hotfix:3.1.0')
}
3、在引入完该引入的依赖包和配置好module环境后,我们需要做的就是代码层的修改了
1)首先我们对Application做自定义处理,即重构Application初始化Sophix;
import android.app.Application;
import android.content.pm.PackageManager;
import android.util.Log;
import com.taobao.sophix.PatchStatus;
import com.taobao.sophix.SophixManager;
import com.taobao.sophix.listener.PatchLoadStatusListener;
/**
* Created by ${Dota.Wang} on 2017/8/22.
*/
public class SophixApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initSophix();
}
private void initSophix() {
String appVersion;
try {
appVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
appVersion = "1.0.0"; //需保持和versionCode一致
e.printStackTrace();
}
// initialize最好放在attachBaseContext最前面
SophixManager.getInstance().setContext(this)
.setAppVersion(appVersion)
.setAesKey(null)
.setEnableDebug(true) //调试状态下true,上线模式下false
.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("code","表明补丁加载成功");
} else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
// 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;
// 建议: 用户可以监听进入后台事件, 然后调用killProcessSafely自杀,以此加快应用补丁,详见1.3.2.3
Log.i("code","用户可以监听进入后台事件, 然后应用自杀");
} else if (code == PatchStatus.CODE_LOAD_FAIL) {
// 内部引擎异常, 推荐此时清空本地补丁, 防止失败补丁重复加载
// SophixManager.getInstance().cleanPatches();
Log.i("code","内部引擎异常, 推荐此时清空本地补丁, 防止失败补丁重复加载");
} else {
// 其它错误信息, 查看PatchStatus类说明
Log.i("code"," 其它错误信息, 查看PatchStatus类说明");
}
}
}).initialize();
// queryAndLoadNewPatch不可放在attachBaseContext 中,否则无网络权限,建议放在后面任意时刻,如onCreate中
SophixManager.getInstance().queryAndLoadNewPatch();
}
}
注:代码中的appVersion 需保持和versionCode一致
2)其次做AndroidMainfest的配置;
详细信息标注如下图:
详细代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dota.recyclerviewdemo">
<!-- 网络权限 -->
<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"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:name=".SophixApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="24594080-1" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="d0ceea78db5ed85e05ef9bdad68e5c93" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCHUc1CphGfqbunQOxdQhtMZBm64TvvTr2blLlFWzpLX7KbKg+lyfytugTBgt+eZ42BTpX0zlIG18TGf4rEx18hF5/ZjVpHnhs7pvD7lzS8kfTAGz3CHUiQgnzF5fblXFGfuwPH8Hy9oQye2vYYNAver33u31Ho8uP63RcDBtqOWXV/tIvfSjMqmI+HmS+sfqcL1PRZH8xiOUZLAIBLdlleu7JiT9eIrgvFs0DRMh/QFVRSeRdfvpgTfM5IgeMBc4EbO9P+ChtOqRT/Ze9GqWlaZ+4qSVWUo84MtUQtD6f5QCUtwnCn6T2QsXPsGpI6dJE66Ki6uKyOAFF4pg+AB8hxAgMBAAECggEAUDZtNdS7Ham/UqVpdt540A+GMQDSRkimnFLMgBBTvdo8RT3piJzy6wgZSZwKchtwBfZPbY3fka7VNdTkrUCBY1xdpNflbA6Mlg+fSYOX/y+FpVqAgVBac07mJ2jpsYvH1qdE1n/3cXDFymSA/FfdsWLVg4NdSniwK0RjH6gwZbBFCr4FpXVXJRDUPyEjfMzm22TEgLx0JUL9OWWIEv4RunfQrmqUddt/wXX60JRgOKkRf4eSKIEdg8gLbdQQhJqtGWpGzGhGffxBYWG14W+lvaNtJdwxH8Jvuq9YFnFG/8Nq0wZdHtz6zPzuHC8C2QjmVJtkRLDaY/bu3TjYbLbjAQKBgQC8E+jiaTTzHc3D00OhbbOFzgGtU15CP/cDZf7FRzPD3AWUMTOE/qMjyve4Tdz1Nys0iFryMXgIJYCENNzXuPNJjilW28B9NcyfWQN/XyyrRBLXtNhkC+lKhwIan5CjBBCHMIER5U5v6SZ6MCD8ySHPQvR4JyeJSI8Uejapd7aiWwKBgQC4ME8jiKxB/BSlOGkmTGRpGDaZcAefa7N72qfjjbV6kWjdaafmx7kn5N6QD6yRauILRzYRdrBhgVllIp66kS2mIIeFoVhBD5CMppM01sq34mWs9gMjU6umi9I/cgVcx5Gad6jNbyNHrzBVFnVfZ/eqKAWHPqghBnzsTrodqpaiIwKBgFcDHwsggU64yyl95BwbFKMi28PEfidIUI9R8Lx0ZI6IjEwzNxKge/ljPZgW7dLE40g6jVjfahjawMppaoKjHms5SgbVRAipizFtjSCwd4oExHLnR/WBTT0dmOuYRuIkT0E8P3p4DAHaa+K7wYcxxmKNJ20vMCtjVk6y+/cLW4NhAoGBAIGoUC1dapVQlF/yI+L3578rclP0zwjkUgaf14uSF+xeLAmg9La6ZZa1wA59WidLrC/wQxlsCaG54moVQtNmm1HUmFHzUCMMh97a68lIYo+raZOl4nOd1Ll6t9g9tZSoT9EC5usmjxlJM6bSFnHAyFEZtzqLWuNUo9/dU5rKmRstAoGBAIt7rzuGDKpT5vPS53zBFoWhSlezFJ45NEfyTqBpxJuBEGzuB2GvxWqiN1aBOxoFQXp1blS67xDYRv//qUlvALKJKJ4nFE/L8OJ2EM4zkHjSrl0VfWDbw6Lb21DHwsIKEBxHAHSNaMM+DbW1V6VkRWLdzlTsop4AqY+5jLceOtfF" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3)再对代码中需要做修复处理的代码做处理。
我们的代码中做了个小变动,即改变布局中TextView中的内容,代码如下:
//修复前
tv.setText("这是修复前的效果!!!");
//修复后
tv.setText("这是修复后的效果!!!");
三、打apk包、打补丁、测试
当我们处理完我们的第二步代码层后,我们需要做的就是给修复前和修复后的2种状态打2个不同的apk包(注意命名,不要混淆了)。
1)对打完的包我们需要做的是打补丁,这里我们使用阿里的补丁工具SophixPatchTool,下载运行SophixPatchTool.exe,添加包:
在打这个补丁的过程中,我们可能会遇到如下问题:
不要慌,这个你查看下log会告诉你问题所在,我遇到的问题是jdk的环境配置问题,当我使用jdk1.8.0_45时,就会出现上图的打补丁失败的情况,而当我换成jdk1.8.0_92时,打补丁成功,no problem,在这里我只能吐槽说,阿里的兼容性也太差劲了。
然后,点击Go开始打补丁,日志会详细打印出补丁情况,也可导出日志。补丁打完后,在输出目录里会有一个sophix-patch.jar,这个就是我们需要的补丁包。
2)接下来,我们需要做的操作是将刚刚打初的补丁上传发布,如下图:
注意,此处的版本号千万不要写错了,要跟代码中配置的appVersion以及打包的versionCode都保持一致,这样你才能在你发布补丁成功后看到修复的效果,否则无解。
当你成功将补丁运行到安装的apk中,并且成功的看到了修复后的效果,阿里的平台后台中将能看到你成功的记录,给你设备+1
这样,再后台这边发布补丁成功后,手机端启动应用就能看到修复的效果了,完美!
当然,代码中还遇到过其他的一些问题,比如在app的build.gradle中添加了传递性依赖,代码如下:
()
compile ('com.aliyun.ams:alicloud-android-hotfix:3.1.0') {
exclude(module:'alicloud-android-utdid')
}
这样写就直接导致我无法打出补丁或者就算补丁出来了也没有修复效果,处理方法就是去掉exclude中的内容,只要第一行的依赖包即可。
正确的使用时间和方式:
传递性依赖utdid这个sdk, 所以不需要重复依赖utdid.但是另一方面其它阿里系SDK也可能依赖了utdid这个SDK, 如果编译期间报utdid重复, 所以此时进行如下处理即可, 关闭传递性依赖:
compile ('com.aliyun.ams:alicloud-android-hotfix:3.1.0') {
exclude(module:'alicloud-android-utdid')
}