AndFix解析——(上)
阿里巴巴前一段時(shí)間開源了他們用來解決線上緊急bug的一款A(yù)ndroid庫(kù)——AndFix
對(duì)Android開發(fā)者來說真是一個(gè)很好的消息。
基于自己的經(jīng)驗(yàn),太長(zhǎng)的文字很少有人可以一口氣看下來的,所以我打算分成多篇來分析?這是這個(gè)庫(kù)解析的第一篇,
我們先看一下其中的Demo代碼,其中調(diào)用加載庫(kù)的代碼如下所示:
/*** 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的構(gòu)造函數(shù)初始化了PatchManager對(duì)象,
PatchManager構(gòu)造函數(shù)
那么PatchManager對(duì)象里面都有什么呢,我們深入其中了解一下。
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(); }原來維持了一個(gè)Context對(duì)象的引用,初始化了AndFixManager對(duì)象,mPatchDir對(duì)象為存放Patch文件的文件夾,初始化了Patch的集合,還有持有ClassLoader的Map
其中一些內(nèi)容的聲明如下:
private final Context mContext; private final AndFixManager mAndFixManager; private final File mPatchDir; private final SortedSet<Patch> mPatchs; private final Map<String, ClassLoader> mLoaders;我們對(duì)構(gòu)造函數(shù)的分析在這里就結(jié)束了,我們先不深入的跟進(jìn)Patch和AndFixManager這兩個(gè)類了。
PatchManager init(String version)
接下來分析PatchManager類中的init(String version)函數(shù), 先來看代碼
public void init(String appVersion) {//如果mPatchDir不存在,則創(chuàng)建文件夾,如果創(chuàng)建失敗,則打印Logif(!this.mPatchDir.exists() && !this.mPatchDir.mkdirs()) {Log.e("PatchManager", "patch dir create error.");} else if(!this.mPatchDir.isDirectory()) {//如果遇到同名的文件,則將該同名文件刪除this.mPatchDir.delete();} else {//在該文件下放入一個(gè)名為_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_這個(gè)文件獲取的ver不是null,而且這個(gè)ver和外部初始化時(shí)傳進(jìn)來的版本號(hào)一致 if(ver != null && ver.equalsIgnoreCase(appVersion)) {this.initPatchs(); } else {this.cleanPatch();sp.edit().putString("version", appVersion).commit(); }先看else內(nèi)的內(nèi)容,else里執(zhí)行力cleanPatch()和把外部初始化的時(shí)候傳進(jìn)來的版本號(hào)放入SharedPreferences里。?cleanPatch()做了什么操作呢,我們跟進(jìn)去看一看
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);//這個(gè)方法的作用就是如果file是文件,則刪除它,如果file是文件夾,則將它和它里面的文件都刪除if(!FileUtil.deleteFile(file)) {Log.e("PatchManager", file.getName() + " delete error.");}} }如源碼中的注釋,就是刪除了之前在那兩個(gè)文件夾下的所有的補(bǔ)丁文件。
現(xiàn)在來分析一下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);} }代碼很簡(jiǎn)單,就是把mPatchDir文件夾下的文件作為參數(shù)傳給了addPatch(File)方法 那this.addPatch(file)做了什么呢
//把擴(kuò)展名為.apatch的文件傳給Patch做參數(shù),初始化對(duì)應(yīng)的Patch, //并把剛初始化的Patch加入到我們之前看到的Patch集合mPatchs中 private Patch addPatch(File file) { Patch patch = null;//擴(kuò)展名是否為".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; }上面的代碼很好理解,此時(shí),我們已經(jīng)完整的走下來了init(String version)這個(gè)方法。 再次出現(xiàn)了Patch這個(gè)類,但是我們依然要把它放在一邊,因?yàn)橛捎谄拗?#xff0c;第一篇不會(huì)分析這個(gè)類。
接下來,我們繼續(xù)跟著demo走,會(huì)執(zhí)行兩個(gè)方法,一個(gè)mPatchManager.loadPatch(),一個(gè)mPatchManager.addPatch(patchFileString),?loadPatch()方法是這個(gè)庫(kù)執(zhí)行替換的核心方法,我會(huì)在以后單獨(dú)寫一篇文章來分析,所以,在這篇文章的最后,我們跟進(jìn)addPatch(String)這個(gè)方法一看究竟
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復(fù)制到dest,只不過阿里用了NIO來復(fù)制文件FileUtil.copyFile(src, dest);//調(diào)用了另外一個(gè)addPatch方法,以文件作為參數(shù)Patch patch = this.addPatch(dest);if(patch != null) {//同樣調(diào)用了loadPatch(Patch)方法this.loadPatch(patch);}}簡(jiǎn)單來說,上面的方法就是將補(bǔ)丁文件復(fù)制到/data/data/{包名}/apatch 目錄內(nèi),如果在OptFile文件夾中存在,則刪除。 然后調(diào)用另外一個(gè)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這個(gè)Set中this.mPatchs.add(patch);} catch (IOException var4) {Log.e("PatchManager", "addPatch", var4);}}return patch; }該方法主要做的事情在注釋中即可了解,到這里,第一篇分析就結(jié)束了。
原文地址: http://yunair.github.io/blog/2015/09/25/AndFix-%E8%A7%A3%E6%9E%90(%E4%B8%8A).html
總結(jié)
以上是生活随笔為你收集整理的AndFix解析——(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爱加密Android APk 原理解析
- 下一篇: AndFix解析——(中)