AndFix
AndFix是一个在线修复bug的解决方案,而不是重新发布Android应用程序。它是以Android库的形式发布的。
Andfix是“Android热修复”的缩写。
AndFix支持Android版本从2.3到7.0,ARM和X86架构,Dalvik和ART runtime, 32位和64位。
AndFix补丁的压缩文件格式是.apatch。它从您自己的服务器发送到客户端,以修复应用程序的错误。
原理
AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法,AndFix只能修复方法级别的bug。
使用教程
1.添加依赖
dependencies {
compile 'com.alipay.euler:andfix:0.5.0@aar'
}
AndFixPatchManager:对Andfix进行封装
/**
* Created by xiaoyehai on 2018/11/26 0026.
* 管理AndFix所有的api
*/
public class AndFixPatchManager {
private static AndFixPatchManager mInstance = null;
private PatchManager mPatchManager = null;
public static AndFixPatchManager getInstance() {
if (mInstance == null) {
synchronized (AndFixPatchManager.class) {
if (mInstance == null) {
mInstance = new AndFixPatchManager();
}
}
}
return mInstance;
}
/**
* 初始化AndFix
*
* @param context
*/
public void initPatch(Context context) {
mPatchManager = new PatchManager(context);
mPatchManager.init(getVersionName(context));
mPatchManager.loadPatch();
}
/**
* 加载patch文件
*
* @param path
*/
public void addPatch(String path) {
if (mPatchManager != null) {
try {
mPatchManager.addPatch(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取应用程序版本名称信息versionName
*
* @param context
* @return 当前应用的版本名称
*/
public static String getVersionName(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionName;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.初始化AndFix
public class AndFixApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//初始化AndFix
initAndFix();
}
private void initAndFix() {
AndFixPatchManager.getInstance().initPatch(this);
}
}
3.先准备一个有bug的release版apk:old.apk
public class MainActivity extends AppCompatActivity {
//补丁文件后缀名
private static final String FILE_END = ".apatch";
//apatch文件路径
private String mPatchDir;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPatchDir = getExternalCacheDir().getAbsolutePath() + "/apatch/";
//创建文件夹
File file = new File(mPatchDir);
if (file == null || !file.exists()) {
file.mkdir();
}
}
/**
* 产生bug代码
*
* @param view
*/
public void creatBug(View view) {
String[] array = {"00", "11", "22", "33"};
Toast.makeText(this, array[4], Toast.LENGTH_SHORT).show(); //数组越界
}
/**
* 点击按钮加载补丁文件,修复bug
* 项目中可以在下载补丁文件之后调用,这里为了演示就把补丁文件放在本地的SD卡中了
*
* @param view
*/
public void fixBug(View view) {
AndFixPatchManager.getInstance().addPatch(getPatchName());
Toast.makeText(this, "修复了bug", Toast.LENGTH_SHORT).show();
}
/**
* 获取patch文件路径
*
* @return
*/
private String getPatchName() {
//AndFix:apatch文件名
return mPatchDir.concat("AndFix").concat(FILE_END);
}
}
4.修复bug后的release版apk:new.apk
public void creatBug(View view) {
String[] array = {"00", "11", "22", "33"};
Toast.makeText(this, array[3], Toast.LENGTH_SHORT).show(); //修复bug后代码
}
5.使用apkpatch-1.0.3.jar工具用命令生成.apatch文件
apkpatch-1.0.3.jar工具可以在github官网下载。
进入apkpatch-1.0.3文件夹文件夹,使用如下命令生成补丁.apatch文件:
apkpatch.bat -f new.apk -t old.apk -o outputs -k lantu.jks -p 123456 -a lantu -e 123456
apkpatch.bat -f 新apk -t 旧apk -o 输出目录 -k app签名文件 -p 签名文件密码 -a 签名文件别名 -e 别名密码
命令行参数说明:
生成的.apatch文件:
5.修复测试
把补丁文件放入内存卡中,发现bug已经被修复。
实际开发中把补丁文件从服务器下载下来,自动加载补丁文件,修复bug。
注意每次appversion变更都会导致所有补丁被删除,如果appversion没有改变,则会加载已经保存的所有补丁。
在需要的地方调用PatchManager的addPatch方法加载新补丁,比如可以在下载补丁文件之后调用。
AndFix在服务中实现
步骤:
1.检查服务端是否有新的apatch文件
2.有:下载apatch文件
3.加载下载好的apatch文件,修复bug
/**
* Created by xiaoyehai on 2018/11/27 0027.
* 1.检查服务端是否有新的apatch文件
* 2.有:下载apatch文件
* 3.加载下载好的apatch文件,修复bug
*/
public class AndFixService extends Service {
public static final String TAG = AndFixService.class.getSimpleName();
private static final int DOWNLOAD_APATCH = 0x01;
private static final int UPDATE_APATCH = 0x02;
public static final String UPDATE_PATCH_URL = "";
public static final String DOWNLOAD_PATCH_URL = "";
//存放apatch文件的目录
private String mPatchFileDir;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case DOWNLOAD_APATCH: //下载apatch文件
downloadPatch();
break;
case UPDATE_APATCH: //检查服务端是否有新的apatch文件
checkApatchUpdate();
break;
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
init();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHandler.sendEmptyMessage(UPDATE_APATCH);
return START_NOT_STICKY; //服务被系统回收之后不会自动重启
}
private void init() {
mPatchFileDir = getExternalCacheDir().getAbsolutePath() + "/apatch/";
File patchDir = new File(mPatchFileDir);
try {
if (patchDir == null || !patchDir.exists()) {
patchDir.mkdirs();
}
} catch (Exception e) {
e.printStackTrace();
stopSelf(); //停止服务
}
}
/**
* 检查服务端是否有新的apatch文件
*/
private void checkApatchUpdate() {
//获取服务器信息,判断是否有新的apatch文件
OkHttpManager.getInstance().asyncJsonStringByURL(UPDATE_PATCH_URL, new OkHttpManager.StringCallback() {
@Override
public void onResponse(String result) {
//有新的apatch文件,下载文件
mHandler.sendEmptyMessage(DOWNLOAD_APATCH);
}
@Override
public void onFailure(IOException e) {
stopSelf();
}
});
}
/**
* 下载apatch文件
*/
private void downloadPatch() {
DownloadManager.getInstance().downloadFile(DOWNLOAD_PATCH_URL, mPatchFileDir, new DownloadManager.FileCallback() {
@Override
public void onSuccess(File file) {
//文件下载成功,加载apatc文件,修复bug
AndFixPatchManager.getInstance().addPatch(file.getAbsolutePath());
}
@Override
public void onProgress(int progress, long total) {
Log.e(TAG, "onProgress: " + progress);
}
@Override
public void onError(Call call, Exception e) {
stopSelf();
}
});
}
}
在app启动页面启动服务:
//启动服务
Intent inten = new Intent(this, AndFixService.class);
startService(inten);