zoukankan      html  css  js  c++  java
  • [Android]AndFix使用说明

    AndFix使用说明

    AndFix,全称是Android hot-fix。是阿里开源的一个热补丁框架,允许APP在不重新发布版本的情况下修复线上的bug。支持Android 2.3 到 6.0,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime,补丁文件是以 .apatch 结尾的文件。

    参考网站:

    github地址

    AndFix使用说明:

    http://www.jianshu.com/p/479b8c7ec3e3

    Alibaba-AndFix Bug热修复框架原理及源码解析 :

    http://blog.csdn.net/qxs965266509/article/details/49816007

    Andfix修复的整体流程:

    AndFix的实现原理是方法的替换:


    具体使用:

    我们以一个具体的例子来说明:
    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文件夹下,

    开始演示效果:


    1.使用gradle添加依赖

    compile 'com.alipay.euler:andfix:0.3.1@aar'
    

    2.使用PatchUtils

    创建一个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);
           }
    }
    

    3.添加权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    

    4.加载补丁

    在需要加载补丁的地方调用:

    PatchUtils.loadPatch(context);
    

    5.生成补丁

    使用工具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。

    可以看到在当前目录下生成了相应的补丁文件:

    补丁命名规则如下:

    6.补丁的命名规则

    a_b.apatch
    

    a表示versionCode,b表示当前的补丁的版本。

    例如:如果当前的versionCode的版本为4,补丁的版本为3,则命名为4_3.apatch。

    7.适用环境说明

    Andfix并不能修复所有情况下出现的bug,测试结果如下:

    该Demo就是用来测试上述这12种情况的。
    例子中都是有bug的情况,请读者自行将bug修复,测试修复情况,使用方法见上面的具体使用

    8.补丁加载的时机

    可以放在自定义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);
        }
    }}
    

    9.补丁加载流程

    10.混淆

    -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.** { *; }
    

    11.局限性

    • 无法添加新类和新的字段
  • 相关阅读:
    Qt 跟踪鼠标事件:setMouseTracking(true)
    Qt setMouseTracking使用
    Qt QGraphicsItem 鼠标点击事件编程方法
    Qt QGraphicsItem信号连接有关问题
    Qt 自定义QGraphicsItem
    Qt 视图框架QGraphicsItem
    Qt QGraphicsItem要点 积累
    Qt Q_UNUSED() 方法的使用
    Qt 绘图之QGraphicsScene QGraphicsView QGraphicsItem详解
    Qt 使用QGraphicsItem绘制复杂的图形
  • 原文地址:https://www.cnblogs.com/xiaomoxian/p/5265920.html
Copyright © 2011-2022 走看看