Android 热修补方案(AndFix)

AndFix介绍

AndFix是一个Android App的在线热补丁框架。使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug。AndFix就是 “Android Hot-Fix”的缩写。

AndFix支持Android 2.3到6.0版本,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime。

AndFix 的补丁文件是以 .apatch 结尾的文件。

AndFix是阿里的开源项目。https://github.com/alibaba/AndFix


小例子:

下载demo APK http://120.55.185.35:8080/old.apk
demo 下载地址,或者扫描下面二维码,安装好apk后运行AndFixDemo,点击“提示信息”按钮,跳出“未修复的toast”,点击“开始修复”,app会到远程服务端下载补丁包更新,大概会持续几秒钟,等待几秒后再次点击“提示信息”按钮,会弹出修复好的内容

实际运行中则不需要点击“开始修复”按钮,在demo中为了对比效果,所以加了按钮去控制
实际运行中检测需要打补丁的方案:
  1. 类似检测升级,在打开app或者某个页面去检测
  2. 服务端推送(需要和服务端定义更多实现细节)
AndFix的优点很明显
  1. 补丁包很小(上面的例子,补丁包才几K),打补丁的速度很快
  2. 打好补丁后,后续都不用再打了

使用方法:

1. 添加依赖

2. 在自定义Application中初始化,并在AndroidManifest.xml中注册该Application

3.在activity 中打补丁,(这里省略了检测步骤,默认就是直接下载补丁并更新,实际开发中需要对补丁的版本进行检测,更新好后删除补丁包,动态获取补丁路径等等)

4.到这里已经完成了配置工作,接下来用正式的key的打包,就生成了old.apk, 把old.apk放到了服务器上,项目就上线了

5.项目上线后,会有各种突发情况,(比如文案修改,紧急bug,造成app crash等麻烦,正常情况只有进行版本升级),这里看下AndFix热修补的步骤,以修改showToast方法为例子

6.现在把showToast方法修改,并打包,命名为fix.apk

7.下载apkpatch工具

8.制作补丁包,cd 到目录下,运行apkpatch.bat -f fix.apk -t old.apk -o output1 -k demo.jks -p 123456 -a demo -e 123456

(这里-t 为老的apk -f为修复过的apk,-o 为输出目录 -k 为打包的key -p -e 为密码 -a 为别名)

9.屏幕输出了增加了修改了toast的方法 ,同时目录下新增了output1 目录,点进去查看,其中.apatch为真正的补丁包

10. 用dex2Jar工具把diff文件转成jar文件,在用jd.gui查看

其实这个工具就是比对两个dex文件,分析出修改过的地方,然后生成补丁包

11. 把.apatch命名为app.apatch上传至服务器,坐等客户端打补丁

源码分析:

1首先来看下在application中的初始化

@Override
    public void onCreate() {
        super.onCreate();
        // 初始化patch管理类
        mPatchManager = new PatchManager(this);
        // 初始化patch版本
        mPatchManager.init("2.0");
        // 加载已经添加到PatchManager中的patch
        mPatchManager.loadPatch();
    }

2 构造了PatchManager对象,来看下代码

public PatchManager(Context context) {
        mContext = context;
        mAndFixManager = new AndFixManager(mContext); //初始化AndFixManager,等会再介绍
        //getFileDir 获取的是/data/data/<application package>/files
        //在这里是 /data/data/<application package>/files/apatch
        mPatchDir = new File(mContext.getFilesDir(), DIR);
        // 支持并发访问的有序的补丁集合
        mPatchs = new ConcurrentSkipListSet<Patch>();
        // ClassLoader的集合,同样也是基于线程安全的
        mLoaders = new ConcurrentHashMap<String, ClassLoader>();
    }

3 初始化patch版本,代码

public void init(String appVersion) {
        //再次检测patch存放路径
        if (!mPatchDir.exists() && !mPatchDir.mkdirs()) {// make directory fail
            Log.e(TAG, "patch dir create error.");
            return;
        } else if (!mPatchDir.isDirectory()) {// not directory
            mPatchDir.delete();
            return;
        }
        //用SharedPreferences 获取path的版本
        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
                Context.MODE_PRIVATE);
        String ver = sp.getString(SP_VERSION, null);
        if (ver == null || !ver.equalsIgnoreCase(appVersion)) {
        //如果是第一次使用或者版本不一致,则删除所有Patch ,这里equalsIgnoreCase是忽略大小写的equals
            cleanPatch();
        // 存放版本
            sp.edit().putString(SP_VERSION, appVersion).commit();
        } else {
        // 加载有所的Patch
            initPatchs();
        }
    }

    private void initPatchs() {
        File[] files = mPatchDir.listFiles();
        for (File file : files) {
            addPatch(file);
        }
    }

4 加载已经添加到PatchManager中的patch,代码

/**
     * load patch,call when application start
     * 这里写的也很清楚了,在程序启动的时候调用
     */
    public void loadPatch() {
        //首先加载了通用的类加载器
        mLoaders.put("*", mContext.getClassLoader());// wildcard
        Set<String> patchNames;
        List<String> classes;
        //遍历每个补丁包  Patch 的结构HashMap<String, List<String>>()
        //实际中,只会有一个key,就是你修改后apk的名字,list中存放修改的className
        //fix----[cv.cocoa.com.andfixdemo.MainActivity_CF]
        for (Patch patch : mPatchs) {
            patchNames = patch.getPatchNames();
            for (String patchName : patchNames) {
                classes = patch.getClasses(patchName);
                //更新补丁
                mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
                        classes);
            }
        }
    }

5.在Application中的初始化代码就完成了,其中还有一个AndFixManager没讲,AndFixManager在构造前,会做一个兼容性的检测,放在了Compat 类中,代码

public class Compat {
    public static boolean isChecked = false;
    public static boolean isSupport = false;

    /**
     * whether support on the device
     * 需要对兼容性进行检测,检测的判断是不能是YunOs的手机,sdk的版本必须是在2.3-6.0之间
     * @return true if the device support AndFix
     */
    public static synchronized boolean isSupport() {
        if (isChecked)
            return isSupport;

        isChecked = true;
        // AndFix.setup()判断是Dalvik还是Art虚拟机,来注册Native方法
        if (!isYunOS() && AndFix.setup() && isSupportSDKVersion()) {
            isSupport = true;
        }

        if (inBlackList()) {
            isSupport = false;
        }

        return isSupport;
    }
}
/**
     * initialize
     *
     * @return true if initialize success
     *
     *  判断是Dalvik还是Art虚拟机,并初始化native的方法,
     *
     * 官方文档中说到https://developer.android.com/guide/practices/verifying-apps-art.html
     * You can verify which runtime is in use by calling System.getProperty("java.vm.version"). If ART is in use, the property‘s value is "2.0.0" or higher.
     *
     */
    public static boolean setup() {
        try {
            final String vmVersion = System.getProperty("java.vm.version");
            boolean isArt = vmVersion != null && vmVersion.startsWith("2");
            int apilevel = Build.VERSION.SDK_INT;
            return setup(isArt, apilevel);
        } catch (Exception e) {
            Log.e(TAG, "setup", e);
            return false;
        }
    }

6.然后再来看下AndFixManager 的代码

//最关键的就是这里,获取到Class对象后,用反射获取修改的方法
private void fixClass(Class<?> clazz, ClassLoader classLoader) {
        //java反射获取Class的方法
        Method[] methods = clazz.getDeclaredMethods();
        MethodReplace methodReplace;
        String clz;
        String meth;
        for (Method method : methods) {
            // 找到MethodReplace的注解
            methodReplace = method.getAnnotation(MethodReplace.class);
            if (methodReplace == null)
                continue;
            //对照上面的demo,clz就是cv.cocoa.com.andfixdemo.MainActivity
            clz = methodReplace.clazz();
            //对照上面的demo,meth 就是showToast
            meth = methodReplace.method();
            if (!isEmpty(clz) && !isEmpty(meth)) {
                //替换方法
                replaceMethod(classLoader, clz, meth, method);
            }
        }
    }

    /**
     * replace method
     *
     * @param classLoader classloader
     * @param clz class
     * @param meth name of target method
     * @param method source method
     */
    private void replaceMethod(ClassLoader classLoader, String clz,
            String meth, Method method) {
        try {
            String key = clz + "@" + classLoader.toString();
            Class<?> clazz = mFixedClass.get(key);
            if (clazz == null) {// class not load
                Class<?> clzz = classLoader.loadClass(clz);
                // initialize target class
                clazz = AndFix.initTargetClass(clzz);
            }
            if (clazz != null) {// initialize class OK
                mFixedClass.put(key, clazz);
                Method src = clazz.getDeclaredMethod(meth,
                        method.getParameterTypes());
                //调用jni的方法
                AndFix.addReplaceMethod(src, method);
            }
        } catch (Exception e) {
            Log.e(TAG, "replaceMethod", e);
        }
    }
时间: 2024-10-14 08:57:26

Android 热修补方案(AndFix)的相关文章

android 热修补之andfix实践

首先有这方面需要的请到https://github.com/xiangzhihong/andfixDemo/tree/master下载例子 对于网上提供的热补丁修复就不多说了,主要有这3种方式可以实现(至于其他的方式,暂不清楚) 1.dexposed     github https://github.com/alibaba/dexposed 2.andfix   github https://github.com/alibaba/AndFix 3.bsdiff  http://blog.csd

Android适配方案小结(三)

在Android适配方案小结(一).(二)中,我们了解了一些基本概念. 那么在具体开发中,我们应该注意什么呢. 首先,我们必须要知道,其实适配的关键在于两点: (1)不同分辨率设备的适配,这点在单位的使用上用dp.sp以及图片资源存放于不同的drawable文件夹就可以解决问题: (2)不同尺寸的适配,这点主要靠将相关值以及布局文件放置于不同的文件夹中来解决. 2.1 values文件夹 可以在工程下创建不同的values文件夹:values-sw480dp, values-sw600dp, v

Android车载方案公司,你该何去何从?

Android车载方案公司,为WinCE工厂服务,这是一种悲哀. 1.你要解释什么是时区,什么是UTC时间,Android的时区设置有什么好处,以及去掉时区设置的坏处. 2.你要解释为什么凯立德中显示的时间和系统时间有可能不一致. 3.手机屏幕投射到车机上,你要解释为什么大部分手机都不行,再深入一点,你要解释什么是WifiDisplay,什么是Airplay,即使能投射,为什么不能反向控制. 4.当你说有付费的方案可以实现手机屏幕投射和反向控制,而我们做不了时,立马有人投来不解甚至不屑的眼神.

【腾讯Bugly干货分享】Android Patch 方案与持续交付

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a31921ac3a1fb613dd40f3 Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本对于开发同学来讲都是一种漫长的煎熬.相比于 iOS 两三天就能达到 80% 的覆盖速度而言,Android 应用版本升级至少需要两周才能达到 80% 的升级率,严重阻碍了版本迭代速度.也导致市场上 App 版本分散,处理 bug 和投

Android视频播放方案选择——深刻分析android平台的视频播放优缺点

https://zhuanlan.zhihu.com/p/27029577?utm_source=qq&utm_medium=social Android我还可以相信你多少系列文章二之音视频播放 音频视频播放在现在的应用里面很常见,传统应用发展到一定阶段多少会引入音视频资源,特别是现在短视频被看作下一个增长爆发点,和之相关的创业层出不穷,作为开发者如何进行音视频技术选型非常关键 MediaPlayer和VideoView给我们提供了非常方便的播放音视频的能力,几乎不需要要写几行代码就可以完成.

和android热修复AndFix技术亲密接触

每次回家都偷懒,不想整理一下,今天周末,强迫自己整理下,内容一定很全. 前言 随着app版本升级迭代,难免有些bug会出现,用户升级新版的代价较高,如果能给app打热补丁,热更新掉app的bug,岂不更好. Andfix andfix是阿里的一个热修复框架,更新至今,已经相对完善了,可以满足我们日常需求.它有很多优点,比如: 1.热修复免重启app 2.更新包小 3.支持360加固(很多blog上说不支持,其实是支持的,下文会介绍怎么用) 至于缺点吗,我不说,哈哈. 下图为热修复图解 使用方式

Android 开发之AndFix

AndFix 阿里巴巴开源项目,地址:https://github.com/alibaba/AndFix tools里面有我们需要的工具,docs是一些文档介绍.AndFix解决在线修复bug,而不是重新发布新版本apk,AndFix支持Android版本从2.3到6.0.我们从下图来了解一下实现原理 修复过程示意图 我们该如何使用这个库呢?首先要添加依赖 //.........Maven 方式........... <dependency> <groupId>com.alipay

Android适配方案小结(二)

该节主要记录从代码中获取与屏幕适配相关的各个参数: Java代码如下 public class ScreenUtil { /** * Note: * 只有activity可以使用getWindowManager,否则应该使用 * Context.getResources().getDisplayMetrics()来获取 */ /** * 获取DisplayMetric相关参数 * @param context * @return */ public static String getMetric

Android适配方案小结(一)

相关计量单位介绍: px:是屏幕的像素点,不同设备显示的效果一样. in:英寸(1英寸等于2.54cm) mm:毫米 pt:磅, 1/72英寸 dp:device independent pixels(设备独立像素),不同设备有不同的显示效果,这个和设备 硬件有关.基于density的抽象单位,如果一个160dpi的屏幕,1dp=1px.(160dpi表示1英寸 有160个像素点) dip:等同于dp,因此我们在xml中使用dp和dip效果一样. sp:scaled pixels, 同dp相似,