AndFix的使用(详解)

来源:互联网 发布:那些软件 新用户 编辑:程序博客网 时间:2024/06/06 18:36

一、Andfix的使用范围(与其他的比较)

图片参考:http://m.blog.csdn.net/alpha58/article/details/74854680
也就是说AndFix存在以下的缺陷:
① 不支持YunOS
② 无法添加新类和新的字段
③ 需要使用加固前的apk制作补丁,但是补丁文件很容易被反编译,也就是修改过的类源码容易泄露。
④ 使用加固平台可能会使热补丁功能失效(看到有人在360加固提了这个问题,自己还未验证)。
⑤ andfix不支持布局资源等的修改
⑥ 官网:AndFix supports Android version from 2.3 to 7.0, both ARM and X86 architecture, both Dalvik and ART runtime, both 32bit and 64bit.
⑦ 应用patch不需要重启。但由于从实现上直接跳过了类初始化,设置为初始化完毕,所以像是静态函数、静态成员、构造函数都会出现问题,复杂点的类Class.forname很可能直接就会挂掉。
⑧ AndFix的一个潜在问题:
    加载一次补丁后,out.apatch文件会copy到getFilesDir目录下的/apatch文件夹中,在下次补丁更新时,会检测补丁是否已经添加在apatch文件夹下,已存在就不会复制加载sdcard的out.apatch。(后面会解决的)

二、自定义签名
参考:http://blog.csdn.net/nimasike/article/details/51457229
三、集成AndFix
1.在app的build.gradle 添加
compile ‘com.alipay.euler:andfix:0.5.0’
2.所需要自定义一个PacthManger.
因为上述存在的问题:加载一次补丁后,out.apatch文件会copy到getFilesDir目录下的/apatch文件夹中,在下次补丁更新时,会检测补丁是否已经添加在apatch文件夹下,已存在就不会复制加载sdcard的out.apatch。所需要自定义一个PacthManger.

public class MyPatchManager {    private static final String TAG = "PatchManager";    // patch extension    private static final String SUFFIX = ".apatch";    private static final String DIR = "apatch";    private static final String SP_NAME = "_andfix_";    private static final String SP_VERSION = "version";    /**     * context     */    private final Context mContext;    /**     * AndFix manager     */    private final AndFixManager mAndFixManager;    /**     * patch directory     */    private final File mPatchDir;    /**     * patchs     */    private final SortedSet<Patch> mPatchs;    /**     * classloaders     */    private final Map<String, ClassLoader> mLoaders;    /**     * @param context     *            context     */    public MyPatchManager(Context context) {        mContext = context;        mAndFixManager = new AndFixManager(mContext);        mPatchDir = new File(mContext.getFilesDir(), DIR);        mPatchs = new ConcurrentSkipListSet<Patch>();        mLoaders = new ConcurrentHashMap<String, ClassLoader>();    }    /**     * initialize     *     * @param appVersion     *            App version     */    public void init(String appVersion) {        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 sp = mContext.getSharedPreferences(SP_NAME,                Context.MODE_PRIVATE);        String ver = sp.getString(SP_VERSION, null);        if (ver == null || !ver.equalsIgnoreCase(appVersion)) {            cleanPatch();            sp.edit().putString(SP_VERSION, appVersion).commit();        } else {            initPatchs();        }    }    private void initPatchs() {        File[] files = mPatchDir.listFiles();        for (File file : files) {            addPatch(file);        }    }    /**     * add patch file     *     * @param file     * @return patch     */    private Patch addPatch(File file) {        Patch patch = null;        if (file.getName().endsWith(SUFFIX)) {            try {                patch = new Patch(file);                mPatchs.add(patch);            } catch (IOException e) {                Log.e(TAG, "addPatch", e);            }        }        return patch;    }    private void cleanPatch() {        File[] files = mPatchDir.listFiles();        for (File file : files) {            mAndFixManager.removeOptFile(file);            if (!FileUtil.deleteFile(file)) {                Log.e(TAG, file.getName() + " delete error.");            }        }    }    /**     * remove all patchs     */    public void removeAllPatch() {        cleanPatch();        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,                Context.MODE_PRIVATE);        sp.edit().clear().commit();    }    /**     * load patch,call when plugin be loaded. used for plugin architecture.</br>     *     * need name and classloader of the plugin     *     * @param patchName     *            patch name     * @param classLoader     *            classloader     */    public void loadPatch(String patchName, ClassLoader classLoader) {        mLoaders.put(patchName, classLoader);        Set<String> patchNames;        List<String> classes;        for (Patch patch : mPatchs) {            patchNames = patch.getPatchNames();            if (patchNames.contains(patchName)) {                classes = patch.getClasses(patchName);                mAndFixManager.fix(patch.getFile(), classLoader, classes);            }        }    }    /**     * load patch,call when application start     *     */    public void loadPatch() {        mLoaders.put("*", mContext.getClassLoader());// wildcard        Set<String> patchNames;        List<String> classes;        for (Patch patch : mPatchs) {            patchNames = patch.getPatchNames();            for (String patchName : patchNames) {                classes = patch.getClasses(patchName);                mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),                        classes);            }        }    }    /**     * load specific patch     *     * @param patch     *            patch     */    private void loadPatch(Patch patch) {        Set<String> patchNames = patch.getPatchNames();        ClassLoader cl;        List<String> classes;        for (String patchName : patchNames) {            if (mLoaders.containsKey("*")) {                cl = mContext.getClassLoader();            } else {                cl = mLoaders.get(patchName);            }            if (cl != null) {                classes = patch.getClasses(patchName);                mAndFixManager.fix(patch.getFile(), cl, classes);            }        }    }    public void addPatch(String path) throws IOException {        /*        *@Description :addPatch,重写这个方法,那是因为源码中的addPatch()方法,        * 在gradle里导入andfix会有个问题,是在原来的项目中,加载一次补丁后,        * out.apatch文件会copy到getFilesDir目录下的/apatch文件夹中,        * 在下次补丁更新时,会检测补丁是否已经添加在apatch文件夹下,        * 已存在就不会复制加载sdcard的out.apatch,        * 所以我们需要对框架中patch文件下的PatchManager类中的addPatch()方法进行修改        *@Author: gaogang6        *@Date :  2017/8/29 15:34        *@Params:  [path]        *@Return: void        */        File src = new File(path);        File dest = new File(mPatchDir, src.getName());        if (!src.exists()) {            throw new FileNotFoundException(path);        }        if (dest.exists()) {            Log.d(TAG, "patch [" + src.getName() + "] has be loaded.");            boolean deleteResult = dest.delete();            if (deleteResult)                Log.e(TAG, "patch [" + dest.getPath() + "] has be delete.");            else {                Log.e(TAG, "patch [" + dest.getPath() + "] delete error");                return;            }        }        FileUtil.copyFile(src, dest);// copy to patch's directory        Patch patch = addPatch(dest);        if (patch != null) {            loadPatch(patch);        }    }}

3、自定义Application

import java.io.File;import java.io.IOException;import android.app.Application;import android.os.Environment;import android.util.Log;/** * sample application *  * @author sanping.li@alipay.com *  */public class MainApplication extends Application {    private static final String TAG = "euler";    private static final String APATCH_PATH = "/out.apatch";//补丁的文件    /**     * patch manager     */    private MyPatchManager mPatchManager;    @Override    public void onCreate() {        super.onCreate();        // initialize        mPatchManager = new MyPatchManager(this);        mPatchManager.init("1.0");        Log.d(TAG, "inited.");        // load patch        mPatchManager.loadPatch();        Log.d(TAG, "apatch loaded.");        // add patch at runtime        try {            // 自己在sdcard中存放.apatch文件的位置            File file=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()                    +File.separator+"gaogang"+File.separator);            if (!file.exists()){                file.mkdir();            }            // 自己在sdcard中存放.apatch文件的位置            String patchFileString = Environment.getExternalStorageDirectory()                    .getAbsolutePath()+File.separator+"gaogang"+ APATCH_PATH;            mPatchManager.addPatch(patchFileString);            Log.d(TAG, "apatch:" + patchFileString + " added.");        } catch (IOException e) {            Log.e(TAG, "打补丁出错了", e);        }    }}

记住在AndroidManifest.xml文件中添加Application
四、如何使用
在实际中,.apatch文件最好是在Loading界面就通过网络下载补丁文件,然后存储到sdcard自己存放的那么目录下面。(Andoid6.0之后需要动态申请存储权限)。
(1)首先:编辑一个
然后使用Build->Build APK。将apk的名字命名为bug.apk
这里写图片描述
再随便修改一下
这里写图片描述

同理将名字命名为nobug.apk
(2)下载一个文件apkpatch.把之前生成的bug.apk和nobug.apk,还有打包所使用的keystore文件放到apkpatch-1.0.3目录下
打开cmd,进入到apkpatch-1.0.3目录下,输入如下指令
apkpatch.bat -f nobug.apk -t bug.apk -o out -k andfix.jks -p 111111 -a gaogang -e 111111
每个参数含义如下
-f 新版本的apk
-t 旧版本的apk
-o 输出apatch文件的文件夹,可以随意命名
-k 打包的keystore文件名
-p keystore的密码
-a keystore 用户别名
-e keystore 用户别名的密码
这里写图片描述

(3)安装有bug.apk
(4)点击显示:

这里写图片描述
(5)将out.apatch文件放入服务器
关闭了,再代开
这里写图片描述

大兄弟,项目下载地址在这里

原创粉丝点击