andfix的基本介绍
https://github.com/alibaba/AndFix
AndFix优劣
1.原理简单,集成简单,使用简单,即使生效
2.只能修复方法及别的bug,极大的限制了使用场景
AndFix执行流程及核心原理
1.添加依赖:compile 'com.alipay.euler:andfix:0.5.0@aar'
2.为什么有的应用需要在project中的build中添加classpath?
APP中的build无论apply还是android中的其他要么是类要么是方法,而这些方法或者类是来自project中classpath中的插件里面的,所以如果只是提供Java代码则只需要添加依赖即可,如果需要添加自定义grade脚本则需要添加classpath
使用AndFix完成线上bug修复
1.封装一个类管理andfix所有的api
public class AndFixPathManager {
private PatchManager mPatchManager=null;
private static AndFixPathManager mInstance = null;
public static AndFixPathManager getInstance() {
if (mInstance == null) {
synchronized (AndFixPathManager.class) {
if (mInstance == null) {
mInstance = new AndFixPathManager();
}
}
}
return mInstance;
}
//初始化AndFix方法
public void initPatch(Context context) {
mPatchManager = new PatchManager(context);
mPatchManager.init(Utils.getVersionName(context));
mPatchManager.loadPatch();
}
//加载我们的patch文件
public void addPatch(String path){
try {
if(mPatchManager!=null) {
mPatchManager.addPatch(path);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2,Utils获取当前版本名和测试输出空指针
public class Utils {
public static String getVersionName(Context context){
String versionName="1.0.0";
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return versionName;
}
public static void printLog() {
String error="";
Log.e("TAG",error);
}
}
新建MyApplication初始化andfix
AndFixPathManager.getInstance().initPatch(this);
准备一个错误apk
1.布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yijia.andfix.MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/kill_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="产生bug"
android:onClick="createBug"
android:textSize="23sp"/>
<Button
android:id="@+id/clean_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:onClick="fixBug"
android:text="解决bug"
android:textSize="23sp"/>
</LinearLayout>
2.printLog方法
public static void printLog() {
String error=null;
Log.e("TAG",error);
}
apatch文件生成
1.去官网下载一个tool(apkpatch-1.0.3)
2.打开apkpatch然后将oldapk和newapk,jks并新建一个输出文件的文件夹outputs
3.使用命令:apkpatch查看命令;使用这个命令生成apkpatch -f new.apk -t old.apk -o outputs/ -k release.jks -p 111111 -a release -e 111111,此时会生成apatch
4.可以创建一个命令脚本:新建一个create_patch.sh 里面输入:sh apkpatch -f new.apk -t old.apk -o outputs/ -k release.jks -p 111111 -a release -e 111111
5.andfix使用 将.apatch下载下来并加载好
public class AndFixService extends Service {
private static final String TAG = AndFixService.class.getSimpleName();
private static final String FILE_END = ".apatch";
private static final int UPDATE_PATCH = 0x02;
private static final int DOWNLOAD_PATCH = 0x01;
private String mPatchFileDir;
private String mPatchFile;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_PATCH:
checkPatchUpdate();
break;
case DOWNLOAD_PATCH:
downloadPatch();
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(UPDATEPATCH); mHandler.sendEmptyMessage(DOWNLOADPATCH); return STARTNOTSTICKY; }
//完成文件目录的构造
private void init() {
mPatchFileDir = getExternalCacheDir().getAbsolutePath() + "/apatch/";
File patchDir = new File(mPatchFileDir);
try {
if (patchDir == null || !patchDir.exists()) {
patchDir.mkdir();
}
} catch (Exception e) {
e.printStackTrace();
stopSelf();
}
}
private BasePatch mBasePatchInfo;
//检查服务器是否有patch文件
private void checkPatchUpdate() {
RequestCenter.requestPatchUpdateInfo(new DisposeDataListener() {
@Override
public void onSuccess(Object responseObj) {
mBasePatchInfo = (BasePatch) responseObj;
if (!TextUtils.isEmpty(mBasePatchInfo.data.downloadUrl)) {
//下载patch文件
mHandler.sendEmptyMessage(DOWNLOAD_PATCH);
} else {
stopSelf();
}
}
@Override
public void onFailure(Object reasonObj) {
stopSelf();
}
});
}
//完成patch文件的下载
private void downloadPatch() {
//初始化patch文件下载路径
mPatchFile = mPatchFileDir.concat(String.valueOf(System.currentTimeMillis())).
concat(FILE_END);
RequestCenter.downloadFile("http://www.ahhuabang.com/data/appfile/yijia.apatch", mPatchFile,
new DisposeDownloadListener() {
@Override
public void onProgress(int progrss) {
Log.d(TAG, "current progedss: " + progrss);
}
@Override
public void onSuccess(Object responseObj) {
//将我们下载好的patch文件添加到我们的andfix中
AndFixPathManager.getInstance().addPatch(mPatchFile);
}
@Override
public void onFailure(Object reasonObj) {
stopSelf();
}
});
}
}
解释一下:RequestCenter.requestPatchUpdateInfo简单post请求,测试的时候可以忽略掉检查步骤,直接下载,封装方法代码太长了,后期会重新封装一个简单方法
主程序代码
public class MainActivity extends AppCompatActivity {
//后缀
private static final String FILE_END = ".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();
}*/
startPatchService();
}
private void startPatchService() {
Intent intent=new Intent(this, AndFixService.class);
startService(intent);
}
public void createBug(View view) {
Utils.printLog();
Toast.makeText(MainActivity.this,"点击了",Toast.LENGTH_LONG).show();
}
public void fixBug(View view) {
AndFixPathManager.getInstance().addPatch(getPatchName());
}
private String getPatchName() {
return mPatchDir.concat("yijia").concat(FILE_END);
}
}