首页 > 代码库 > 和android热修复AndFix技术亲密接触
和android热修复AndFix技术亲密接触
每次回家都偷懒,不想整理一下,今天周末,强迫自己整理下,内容一定很全。
前言
随着app版本升级迭代,难免有些bug会出现,用户升级新版的代价较高,如果能给app打热补丁,热更新掉app的bug,岂不更好。
Andfix
andfix是阿里的一个热修复框架,更新至今,已经相对完善了,可以满足我们日常需求。它有很多优点,比如:
1.热修复免重启app
2.更新包小
3.支持360加固(很多blog上说不支持,其实是支持的,下文会介绍怎么用)
至于缺点吗,我不说,哈哈。
下图为热修复图解
使用方式
1.在android studio里添加依赖
compile ‘com.alipay.euler:andfix:0.5.0@aar‘
1.在Application的onCreate方法中初始化andfix
// 初始化patch管理类
mPatchManager = new PatchManager(this);
// 初始化patch版本
String appVersion = "1.0";
try {
appVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;//获取app版本号
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
mPatchManager.init(appVersion);
// 加载已经添加到PatchManager中的patch
mPatchManager.loadPatch();
在热修复版本时,appversion不用变。
页面布局
为了方便测试,主页面放3个button,第一个用来显示是否有bug,第二个用来从网络上下载热修复包(需自建web服务),然后热修复,第三个用来从本地文件中加载热修复包(无需自建web服务)。
代码
public class MainActivity extends AppCompatActivity {
private MyApplication app;
private static final String LOCAL_NAME = "local.apatch";//本地SD卡中的更新文件[2选1]
private static final String NET_NAME = "net.apatch";//网络上的更新文件[2选1]
private final String url="http://192.168.1.2/net.apatch";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//用于模拟是否有bug
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "出现bug", Toast.LENGTH_SHORT).show();
// Toast.makeText(MainActivity.this, "bug已经修复", Toast.LENGTH_SHORT).show();
//[第二个版本注释第一行,取消第二行注释]
}
});
//网络下载并热修复
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
fix();
}
});
//本地热修复
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
update();
}
});
}
.....
更新的两个方法
//本地热更新
private void update() {
String patchFileStr = Environment.getExternalStorageDirectory().getAbsolutePath() +File.separator+ LOCAL_NAME;
try {
MyApplication.mPatchManager.addPatch(patchFileStr);
} catch (IOException e) {
e.printStackTrace();
}
}
//网络下载热更新
private void fix() {
new Thread(){
@Override
public void run() {
try {
String patchFileStr = Environment.getExternalStorageDirectory().getAbsolutePath();
downLoadFromUrl(url, NET_NAME, patchFileStr);
MyApplication.mPatchManager.addPatch(patchFileStr);//热修复
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
为了减少代码量,这里网络请求没有使用框架,而是自己写的网络请求方法
/**
* 从网络Url中下载文件
*
* @param urlStr
* @param fileName
* @param savePath
* @throws IOException
*/
public static void downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时间为3秒
conn.setConnectTimeout(3 * 1000);
//防止屏蔽程序抓取而返回403错误
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
//得到输入流
InputStream inputStream = conn.getInputStream();
//获取自己数组
byte[] getData = http://www.mamicode.com/readInputStream(inputStream);"hljs-comment">//文件保存位置
File saveDir = new File(savePath);
if (!saveDir.exists()) {
saveDir.mkdir();
}
File file = new File(saveDir + File.separator + fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(getData);
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
System.out.println("info:" + url + " download success");
}
/**
* 从输入流中获取字节数组
*
* @param inputStream
* @return
* @throws IOException
*/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
编译
项目编译需要自备签名文件,如果没有的话,自己创建一个
生成含bug的apk
将生成出的app-release.apk文件重命名为bug.apk
生成解决bug的apk
把出现bug的那条toast注释掉,改为bug已修复的toast
再次编译将生成的app-release.apk文件重命名为fixedbug.apk
生成apatch
这里需要去下载工具
https://github.com/alibaba/AndFix
tools文件夹里是工具,下载到本地
顺便将刚刚两个bug.apk和fixedbug.apk以及签名文件放入文件夹
因为我们只有一个fixedbug.apk所以我们用下面的生成代码
apkpatch -f <new> -t <old> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
-a,--alias <alias> keystore entry alias.
-e,--epassword <***> keystore entry password.
-f,--from <loc> new Apk file path.
-k,--keystore <loc> keystore path.
-n,--name <name> patch name.
-o,--out <dir> output dir.
-p,--kpassword <***> keystore password.
-t,--to <loc> old Apk file path.
具体做法
1.先打开cmd或者终端,cd到当前目录下
2.敲入
apkpatch-1.0.3/apkpatch.sh -f fixedbug.apk -t bug.apk -o Out -k a.jks -p 123456 -a 654321 -e 654321
3.这个时候这个out文件夹下apatch文件就是热更新包
4.将此文件改名net.apatch放入www目录下
保证刚刚的http://192.168.1.2/net.apatch路径可以访问
开始测试
先安装bug.apk到手机上
1.打开ddms
2.将net.apatch复制一份命名为local.apatch拉入手机中
可以顺便将bug.apk也放入手机,方便安装(或其它方式安装bug.apk)
3.打开app,运行如图
出现bug
4.点击本地文件热修复,然后再次点击显示
提示bug已修复
至此从本地文件热修复演示完成
5.卸载app,重新安装然后点击显示,出现bug,点击网络下载热修复,再次点击显示,提示bug已修复(同上图)
总结
至此两种方法热修复均解决了appbug,代码不复杂,需要源码请留言。
补充
当团队协作时,多个小组生成出多个apatch文件,可以使用工具合并
apkpatch -m <apatch_path...> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
-a,--alias <alias> keystore entry alias.
-e,--epassword <***> keystore entry password.
-k,--keystore <loc> keystore path.
-m,--merge <loc...> path of .apatch files.
-n,--name <name> patch name.
-o,--out <dir> output dir.
-p,--kpassword <***> keystore password.
混淆代码
-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
native <methods>;
}
和android热修复AndFix技术亲密接触