AndFix,全称是Android hot-fix。是阿里开源的一个热补丁框架,容许APP在不从新发布版本的状况下修复线上的bug。支持Android 2.3 到 6.0,而且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime,补丁文件是以 .apatch 结尾的文件。java
参考网站:android
github地址git
AndFix使用说明:github
http://www.jianshu.com/p/479b8c7ec3e3网络
Alibaba-AndFix Bug热修复框架原理及源码解析 :架构
[http://blog.csdn.net/qxs965266509/article/details/49816007](http://blog.csdn.net/qxs965266509/article/details/49816007 " Alibaba-AndFix Bug热修复框架原理及源码解析 ")app
Andfix修复的总体流程:框架
AndFix的实现原理是方法的替换:ide
咱们以一个具体的例子来讲明:
demo测试activity生命周期函数界面很简单,只是一个textView加button,当前textView显示的是有bug,那么点击button以后,就会加载补丁,textView就会显示bug第一次被修复了。函数
先给出TestActivityLifeCycle.java:
package com.tulipsport.android.andfixdemos.test_activity; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import com.tulipsport.android.andfixdemos.R; import com.tulipsport.android.andfixdemos.utils.PatchUtils; public class TestActivityLifeCycle extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView text=(TextView)findViewById(R.id.test); text.setText("有bug"); findViewById(R.id.button).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ PatchUtils.loadPatch(getApplicationContext()); } }); }
}
按照下面1-3步执行,而后打包生成一个“有bug”的apk文件,咱们将它命名为1.apk。
而后,将上面的 text.setText("有bug");
换成 text.setText("bug第一次被修复了");
再次打包,生成apk文件,咱们将它命名为2.apk。而后使用apkpatch工具,根据1.apk和2.apk生成补丁文件,有关 补丁文件的生成操做和命名方式,见5-6.
假设咱们生成的补丁为2_1.apatch,如今来演示效果。
将有bug的apk安装到模拟器上:
打开app:显示有bug
咱们将补丁文件放在sdcard根目录下的tulipsport_patches文件夹下,
开始演示效果:
compile 'com.alipay.euler:andfix:0.3.1@aar'
建立一个PatchUtils的工具类,用于加载补丁。
public class PatchUtils{ private static final String TAG="euler"; private static final String TULIPSPORT_PATCHES="/tulipsport_patches"; private static final String DIR="apatch";//补丁文件夹 /** * patch manager */ public static PatchManager mPatchManager; public static void loadPatch(Context context){ mPatchManager=new PatchManager(context); mPatchManager.init(getVersionName(context)); mPatchManager.loadPatch(); try { File dir=new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + TULIPSPORT_PATCHES); String loadPatchName=Environment.getExternalStorageDirectory() .getAbsolutePath() + TULIPSPORT_PATCHES + "/" + String.valueOf(getVersionCode(context)) + "_" + FileUtils.getLoadPatchName(dir, "apatch",String.valueOf(getVersionCode(context))) + ".apatch"; Log.d("loadPatchName",loadPatchName); mPatchManager.addPatch(loadPatchName); Log.d(TAG,"apatch:" + loadPatchName + " added."); //复制且加载补丁成功后,删除下载的补丁 File f=new File(context.getFilesDir(),DIR); if (f.exists()) { boolean result=new File(loadPatchName).delete(); if (!result) Log.e(TAG,loadPatchName + " delete fail"); } } catch (Exception e) { e.printStackTrace(); } } private static int getVersionCode(Context context){ try { PackageInfo pi=context.getPackageManager().getPackageInfo(context.getPackageName(),0); return pi.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return 0; } } private static String getVersionName(Context context){ try { PackageInfo pi=context.getPackageManager().getPackageInfo(context.getPackageName(),0); return pi.versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return null; } }}
注意:补丁的下载位置为sdcard根目录下的tulipsport_patches文件夹,能够自行修改。
PatchUtils中使用了FileUtils类,在这里简单给出FileUtils:
package com.tulipsport.android.andfixmorebugs; import java.io.File; import java.util.ArrayList; import java.util.List; /** * Created by Brooks on 2016/3/9. */ public class FileUtils{ /** * @param fileDir 文件目录 * @param fileType 后缀名 * @return 特定目录下的全部后缀名为fileType的文件列表 */ public static List<String> getFiles(File fileDir,String fileType) throws Exception{ List<String> lfile=new ArrayList<String>(); File[] fs=fileDir.listFiles(); for (File f : fs) { if (f.isFile()) { if (fileType .equals(f.getName().substring( f.getName().lastIndexOf(".") + 1, f.getName().length()))) lfile.add(f.getName()); } } return lfile; } public static String getLoadPatchName(File fileDir,String fileType,String versionCode) throws Exception{ List<String> files=getFiles(fileDir,fileType); int maxPatchVersion=0; for (String name : files) { if (name.startsWith(versionCode + "_")) { int patchVersion=Integer.valueOf(name.substring(name.indexOf("_") + 1,name.indexOf("."))); maxPatchVersion=Math.max(maxPatchVersion,patchVersion); } } return String.valueOf(maxPatchVersion); } }
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
在须要加载补丁的地方调用:
PatchUtils.loadPatch(context);
使用工具apkpatch-1.0.3
下载地址:https://github.com/alibaba/AndFix/raw/master/tools/apkpatch-1.0.3.zip
使用命令apkpatch生成补丁。
图示参数缺一不可,不然没法生成补丁!!!
例如:
旧的apk为1.apk,新的apk为2.apk, -o表示补丁的输出目录,-k表示keystore, -p表示keystore的密码,-a表示alias, -e表示entry password。
能够看到在当前目录下生成了相应的补丁文件:
补丁命名规则以下:
a_b.apatch
a表示versionCode,b表示当前的补丁的版本。
例如:若是当前的versionCode的版本为4,补丁的版本为3,则命名为4_3.apatch。
Andfix并不能修复全部状况下出现的bug,测试结果以下:
该Demo就是用来测试上述这12种状况的。
例子中都是有bug的状况,请读者自行将bug修复,测试修复状况,使用方法见上面的具体使用。
能够放在自定义Application的onCreate方法中,也能够放在button的点击事件中,也能够放在监听网络变化的广播中。
例如:
放在自定义Application中:
package com.tulipsport.android.andfixmorebugs; import android.app.Application; /** * Created by Brooks on 2016/3/4. */ public class MyApplication extends Application{ @Override public void onCreate(){ super.onCreate(); PatchUtils.loadPatch(getApplicationContext()); } }
或者放在监听网络变化广播中:
public class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context,Intent intent){ ConnectivityManager connectivityManager=(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { Toast.makeText(context,"网络不能够用",Toast.LENGTH_SHORT).show(); //改变背景或者 处理网络的全局变量 } else { //改变背景或者 处理网络的全局变量 //这里开始执行下载补丁操做,下载完成后,开始加载补丁 Toast.makeText(context,"开始加载补丁",Toast.LENGTH_SHORT).show(); PatchUtils.loadPatch(context); } }}
-printmapping proguard.map
首先须要生成mapping文件记录混淆规则,以后能够把printmapping 这句话注释掉,每次只使用applymapping。
-applymapping proguard.map
而后在下面加上
-keep class * extends java.lang.annotation.Annotation -keepclasseswithmembernames class * { native <methods>; } -keep class com.alipay.euler.andfix.** { *; }