黄沙百战穿金甲,不破楼兰终不还。这篇文章主要讲述和android热修复AndFix技术亲密接触相关的知识,希望能为你提供帮助。
每次回家都偷懒,
不想整理一下,
今天周末,
强迫自己整理下,
内容一定很全。
前言 随着app版本升级迭代,
难免有些bug会出现,
用户升级新版的代价较高,
如果能给app打热补丁,
热更新掉app的bug,
岂不更好。
Andfix andfix是阿里的一个热修复框架,
更新至今,
已经相对完善了,
可以满足我们日常需求。它有很多优点,
比如:
1.热修复免重启app
2.更新包小
3.支持360加固(
很多blog上说不支持,
其实是支持的,
下文会介绍怎么用)
至于缺点吗,
我不说,
哈哈。
下图为热修复图解
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I53J4-0.jpg)
文章图片
使用方式 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服务) 。
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I55453-1.jpg)
文章图片
代码
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 =
readInputStream(inputStream);
//文件保存位置
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();
}
编译 项目编译需要自备签名文件, 如果没有的话, 自己创建一个
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I560M-2.jpg)
文章图片
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I5K60-3.jpg)
文章图片
生成含bug的apk
将生成出的app-release.apk文件重命名为bug.apk
生成解决bug的apk
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I53548-4.jpg)
文章图片
把出现bug的那条toast注释掉, 改为bug已修复的toast
再次编译将生成的app-release.apk文件重命名为fixedbug.apk
生成apatch 这里需要去下载工具
https://github.com/alibaba/AndFix
tools文件夹里是工具, 下载到本地
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I5I56-5.jpg)
文章图片
顺便将刚刚两个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
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I51c1-6.jpg)
文章图片
3.这个时候这个out文件夹下apatch文件就是热更新包
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I53033-7.jpg)
文章图片
4.将此文件改名net.apatch放入www目录下
保证刚刚的http://192.168.1.2/net.apatch路径可以访问
开始测试 先安装bug.apk到手机上
1.打开ddms
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I5FK-8.jpg)
文章图片
2.将net.apatch复制一份命名为local.apatch拉入手机中
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I51O4-9.jpg)
文章图片
可以顺便将bug.apk也放入手机, 方便安装( 或其它方式安装bug.apk)
3.打开app, 运行如图
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I51521-10.jpg)
文章图片
出现bug
4.点击本地文件热修复, 然后再次点击显示
![和android热修复AndFix技术亲密接触](http://img.readke.com/220414/003I540H-11.jpg)
文章图片
提示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.
【和android热修复AndFix技术亲密接触】混淆代码
-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
native <
methods>
;
}
推荐阅读
- Android RecyclerView单击长按事件标准实现(基于OnItemTouchListener + GestureDetector)
- android Button点击事件总结
- Android开发之ViewPager的简单使用
- android aidl
- Android 控件知识点
- Android 回调接口是啥,回调机制详解(zhuan)
- Android 开发组件
- Android开发--adb,SQLite数据库运用
- android 一分钟掌握圆形布局原理--圆形菜单控件 so easy