AndFix解析——(上)
阿里巴巴前一段時間開源了他們用來解決線上緊急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
總結
以上是生活随笔為你收集整理的AndFix解析——(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爱加密Android APk 原理解析
- 下一篇: AndFix解析——(中)