[关闭]
@linux1s1s 2017-01-22T08:54:41.000000Z 字数 3837 阅读 1821

Android 在线热修复框架 AndFix 初步 三

AndroidExtend 2016-03


上一篇博文介绍了patch补丁是如何制作的,这里先从Demo入手,深入源码看看补丁到底是如何生效的。

先来看看Demo,在Application的onCreate()生命周期中加入以下代码。

  1. private static final String APATCH_PATH = "/out.apatch";
  2. @Override
  3. public void onCreate()
  4. {
  5. super.onCreate();
  6. /*
  7. patch manager
  8. */
  9. PatchManager patchManager = new PatchManager(this);
  10. patchManager.init("1.0");
  11. // load patch
  12. patchManager.loadPatch();
  13. // add patch at runtime
  14. try {
  15. // apatch file path
  16. String patchFileString = Environment.getExternalStorageDirectory()
  17. .getAbsolutePath() + APATCH_PATH;
  18. patchManager.addPatch(patchFileString);
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. }

L10 PatchManager管理类构造器

  1. public PatchManager(Context context) {
  2. this.mContext = context;
  3. this.mAndFixManager = new AndFixManager(this.mContext);
  4. this.mPatchDir = new File(this.mContext.getFilesDir(), "apatch");
  5. this.mPatchs = new ConcurrentSkipListSet();
  6. this.mLoaders = new ConcurrentHashMap();
  7. }

PatchManager管理类 组合了AndFixManager管理类,apatch补丁存放目录和两个容器。回到Application中的L11 初始化操作

  1. public void init(String appVersion) {
  2. if(!this.mPatchDir.exists() && !this.mPatchDir.mkdirs()) {
  3. Log.e("PatchManager", "patch dir create error.");
  4. } else if(!this.mPatchDir.isDirectory()) {
  5. this.mPatchDir.delete();
  6. } else {
  7. SharedPreferences sp = this.mContext.getSharedPreferences("_andfix_", 0);
  8. String ver = sp.getString("version", (String)null);
  9. if(ver != null && ver.equalsIgnoreCase(appVersion)) {
  10. this.initPatchs();
  11. } else {
  12. this.cleanPatch();
  13. sp.edit().putString("version", appVersion).commit();
  14. }
  15. }
  16. }

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

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

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

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

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

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

  1. private void initPatchs() {
  2. File[] files = this.mPatchDir.listFiles();
  3. File[] arr$ = files;
  4. int len$ = files.length;
  5. for(int i$ = 0; i$ < len$; ++i$) {
  6. File file = arr$[i$];
  7. this.addPatch(file);
  8. }
  9. }

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

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

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

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

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

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

  1. private Patch addPatch(File file) {
  2. Patch patch = null;
  3. if(file.getName().endsWith(".apatch")) {
  4. try {
  5. patch = new Patch(file);
  6. //把从file文件生成的patch加入到mPatchs这个Set中
  7. this.mPatchs.add(patch);
  8. } catch (IOException var4) {
  9. Log.e("PatchManager", "addPatch", var4);
  10. }
  11. }
  12. return patch;
  13. }

到这里,我们分析完成了Demo中的AndFix方法,留下来三个坑,分别是一个方法mPatchManager.loadPatch()方法和两个类AndFixManagerPatch类,等到后面再补上。

附注: 此文章转载并微修改自AndFix解析,特此说明。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注