AndFix解析——(上)


アリババはこの間、オンラインの緊急バグを解決するためのAndroidライブラリをオープンしました.AndFixAndroid開発者にとっては本当に良いニュースです.
自分の経験に基づいて、長すぎる文字を一気に見る人は少ないので、複数編に分けて分析するつもりです.これはこのライブラリ解析の最初の編です.
まず、その中の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;

	@Override
	public void onCreate() {
		super.onCreate();
		// initialize
		mPatchManager = new PatchManager(this);
		mPatchManager.init("1.0");
		Log.d(TAG, "inited.");

		// load patch
		mPatchManager.loadPatch();
		Log.d(TAG, "apatch loaded.");

		// add patch at runtime
		try {
			// .apatch file path
			String 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の2つのクラスに深くフォローしません.
PatchManager init(String version)
次にPatchManagerクラスのinit(String version)関数を分析し、まずコードを見てみましょう.
public void init(String appVersion) {
	//  mPatchDir   ,      ,      ,   Log
	if(!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.");
        }
    }
}

ソースコードのコメントのように、以前の2つのフォルダの下にあったすべてのパッチファイルが削除されます.
では、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に従って歩き続け、2つの方法を実行します.1つのmPatchManager.loadPatch()、1つの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ディレクトリにパッチファイルをコピーし、OptiFileフォルダに存在する場合は削除します.次に、別のaddPatch(File)メソッドを呼び出し、次にloadPatch()メソッドを再ロードします.
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