阿里巴巴前一段时间开源了他们用来解决线上紧急bug的一款Android库——AndFix

对Android开发者来说真是一个很好的消息。

基于自己的经验,太长的文字很少有人可以一口气看下来的,所以我打算分成多篇来分析 这是这个库解析的第一篇,

我们先看一下其中的Demo代码,其中调用加载库的代码如下所示:

/** * 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 PatchManager mPatchManager;@Overridepublic void onCreate() {super.onCreate();// initializemPatchManager = new PatchManager(this);mPatchManager.init("1.0");Log.d(TAG, "inited.");// load patchmPatchManager.loadPatch();Log.d(TAG, "apatch loaded.");// add patch at runtimetry {// .apatch file pathString patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;mPatchManager.addPatch(patchFileString);Log.d(TAG, "apatch:" + patchFileString + " added.");} catch (IOException e) {Log.e(TAG, "", e);}}}

可以看到代码中首先通过PatchManager的构造函数初始化了PatchManager对象,

PatchManager构造函数

那么PatchManager对象里面都有什么呢,我们深入其中了解一下。

public PatchManager(Context context) {    this.mContext = context;this.mAndFixManager = new AndFixManager(this.mContext);this.mPatchDir = new File(this.mContext.getFilesDir(), "apatch");this.mPatchs = new ConcurrentSkipListSet();this.mLoaders = new ConcurrentHashMap();}

原来维持了一个Context对象的引用,初始化了AndFixManager对象,mPatchDir对象为存放Patch文件的文件夹,初始化了Patch的集合,还有持有ClassLoader的Map

其中一些内容的声明如下:

private final Context mContext;private final AndFixManager mAndFixManager;private final File mPatchDir;private final SortedSet<Patch> mPatchs;private final Map<String, ClassLoader> mLoaders;

我们对构造函数的分析在这里就结束了,我们先不深入的跟进Patch和AndFixManager这两个类了。

PatchManager init(String version)

接下来分析PatchManager类中的init(String version)函数, 先来看代码

public void init(String appVersion) {//如果mPatchDir不存在,则创建文件夹,如果创建失败,则打印Logif(!this.mPatchDir.exists() && !this.mPatchDir.mkdirs()) {    Log.e("PatchManager", "patch dir create error.");    } else if(!this.mPatchDir.isDirectory()) {//如果遇到同名的文件,则将该同名文件删除    this.mPatchDir.delete();    } else {//在该文件下放入一个名为_andfix_的SharedPreferences文件,    SharedPreferences sp = this.mContext.getSharedPreferences("_andfix_", 0);    String ver = sp.getString("version", (String)null);    if(ver != null && ver.equalsIgnoreCase(appVersion)) {    this.initPatchs();    } else {    this.cleanPatch();    sp.edit().putString("version", appVersion).commit();    }}}

接下来我们分析上面代码中的如下代码

//如果从_andfix_这个文件获取的ver不是null,而且这个ver和外部初始化时传进来的版本号一致if(ver != null && ver.equalsIgnoreCase(appVersion)) {this.initPatchs();} else {this.cleanPatch();sp.edit().putString("version", appVersion).commit();}

先看else内的内容,else里执行力cleanPatch()和把外部初始化的时候传进来的版本号放入SharedPreferences里。 cleanPatch()做了什么操作呢,我们跟进去看一看

private void cleanPatch() {//获取mPatchDir目录下所有文件    File[] files = this.mPatchDir.listFiles();    File[] arr$ = files;    int len$ = files.length;    for(int i$ = 0; i$ < len$; ++i$) {        File file = arr$[i$];        //将此文件从OptFile文件夹删除        this.mAndFixManager.removeOptFile(file);        //这个方法的作用就是如果file是文件,则删除它,如果file是文件夹,则将它和它里面的文件都删除        if(!FileUtil.deleteFile(file)) {            Log.e("PatchManager", file.getName() + " delete error.");        }    }}

如源码中的注释,就是删除了之前在那两个文件夹下的所有的补丁文件。

现在来分析一下this.initPatchs()做了什么事

private void initPatchs() {    File[] files = this.mPatchDir.listFiles();    File[] arr$ = files;    int len$ = files.length;    for(int i$ = 0; i$ < len$; ++i$) {        File file = arr$[i$];        this.addPatch(file);    }}

代码很简单,就是把mPatchDir文件夹下的文件作为参数传给了addPatch(File)方法 那this.addPatch(file)做了什么呢

//把扩展名为.apatch的文件传给Patch做参数,初始化对应的Patch,//并把刚初始化的Patch加入到我们之前看到的Patch集合mPatchs中private Patch addPatch(File file) {    Patch patch = null;    //扩展名是否为".apatch"    if(file.getName().endsWith(".apatch")) {        try {            patch = new Patch(file);            this.mPatchs.add(patch);        } catch (IOException var4) {            Log.e("PatchManager", "addPatch", var4);        }    }    return patch;}

上面的代码很好理解,此时,我们已经完整的走下来了init(String version)这个方法。 再次出现了Patch这个类,但是我们依然要把它放在一边,因为由于篇幅限制,第一篇不会分析这个类。

接下来,我们继续跟着demo走,会执行两个方法,一个mPatchManager.loadPatch(),一个mPatchManager.addPatch(patchFileString) loadPatch()方法是这个库执行替换的核心方法,我会在以后单独写一篇文章来分析,所以,在这篇文章的最后,我们跟进addPatch(String)这个方法一看究竟

public void addPatch(String path) throws IOException {    File src = new File(path);    File dest = new File(this.mPatchDir, src.getName());    if(dest.exists()) {    //在mPatchDir文件夹下存在该文件,则AndFixManager移除该文件        this.mAndFixManager.removeOptFile(dest);    }//将文件从src复制到dest,只不过阿里用了NIO来复制文件    FileUtil.copyFile(src, dest);    //调用了另外一个addPatch方法,以文件作为参数    Patch patch = this.addPatch(dest);    if(patch != null) {    //同样调用了loadPatch(Patch)方法        this.loadPatch(patch);    }}

简单来说,上面的方法就是将补丁文件复制到/data/data/{包名}/apatch 目录内,如果在OptFile文件夹中存在,则删除。 然后调用另外一个addPatch(File)方法,然后loadPatch() 下面,我们来看一下重载的addPatch(File)方法

private Patch addPatch(File file) {    Patch patch = null;    if(file.getName().endsWith(".apatch")) {        try {            patch = new Patch(file);            //把从file文件生成的patch加入到mPatchs这个Set中            this.mPatchs.add(patch);        } catch (IOException var4) {            Log.e("PatchManager", "addPatch", var4);        }    }    return patch;}

该方法主要做的事情在注释中即可了解,到这里,第一篇分析就结束了。


原文地址: http://yunair.github.io/blog/2015/09/25/AndFix-%E8%A7%A3%E6%9E%90(%E4%B8%8A).html

更多相关文章

  1. Android(安卓)逆向apk程序的心得
  2. android调试工具adb命令大全
  3. 关于android中使用new Message的内存泄露问题
  4. Android(安卓)Zip文件解压缩代码
  5. Android软件广告屏蔽方法及代码
  6. 浅谈Java中Collections.sort对List排序的两种方法
  7. NPM 和webpack 的基础使用
  8. Python list sort方法的具体使用
  9. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程

随机推荐

  1. MySQL 触发器定义与用法简单实例
  2. 往MySQL中存储图片的方法
  3. 利用MySQL系统数据库做性能负载诊断的方
  4. mysql关联两张表时的编码问题及解决办法
  5. 使用python连接mysql数据库之pymysql模块
  6. mysql字符串拼接并设置null值的实例方法
  7. mysql5.5.28安装教程 超详细!
  8. mysql 5.7.24 压缩包安装配置方法图文教
  9. windows下mysql 8.0.12安装步骤及基本使
  10. windows 10 下mysql-8.0.17-winx64的安装