Android 熱更新是如何實現的?


       Android開發中,我們常常遇到熱更新這個概念,而這個熱更新具體是怎么實現的呢?今天在網上看到一個大神分享的熱更新相關實現原理和實現代碼,感覺灰常不錯,分享給廣大碼農盆友look look 。

 

ClassLoader

      Android的基礎語言是Java,而Java在運行時加載對應的類是通過ClassLoader來實現的,ClassLoader本身是一個抽象來,Android中使用PathClassLoader類作為Android的默認的類加載器。

      PathClassLoader又是什么?PathClassLoader其實實現的就是簡單的從文件系統中加載類文件。PathClassLoade本身繼承自BaseDexClassLoader,BaseDexClassLoader重寫了findClass方法,該方法是ClassLoader的核心,具體代碼如下:

 

@Override

protected Class<?> findClass(String name) throws ClassNotFoundException {

      List<Throwable> suppressedExceptions = new ArrayList<Throwable>();

      Class c = pathList.findClass(name, suppressedExceptions);

      if (c == null) {

           ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class /"" + name + "/" on path: " + pathList);

           for (Throwable t : suppressedExceptions) {

                 cnfe.addSuppressed(t);

           }

           throw cnfe;

      }

      return c;

}

看上面的代碼,我們可以清楚,BaseDexClassLoader將findClass方法委托給了pathList對象的findClass方法,pathList對象是在BaseDexClassLoader的構造函數中new出來的,它的類型是DexPathList。看下DexPathList.findClass源碼是如何做的:

 

public Class findClass(String name, List<Throwable> suppressed) {

      for (Element element : dexElements) {

           DexFile dex = element.dexFile;

           if (dex != null) {

                 Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);

                 if (clazz != null) {

                      return clazz;

                 }

           }

      }

      if (dexElementsSuppressedExceptions != null) {

           suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));

      }

      return null;

}

直接就是遍歷dexElements列表,然后通過調用element.dexFile對象上的loadClassBinaryName方法來加載類,如果返回值不是null,就表示加載類成功,會將這個Class對象返回。而dexElements對象是在DexPathList類的構造函數中完成初始化的。

 

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);

makeDexElements所做的事情就是遍歷傳遞來的dexPath,然后一次加載每個dex文件。

 

上面分析了Android中的類的加載的流程,可以看出來DexPathList對象中的dexElements列表是類加載的一個核心,一個類如果能被成功加載,那么它的dex一定會出現在dexElements所對應的dex文件中,並且dexElements中出現的順序也很重要,在dexElements前面出現的dex會被優先加載,一旦Class被加載成功,就會立即返回,也就是說,我們的如果想做hotpatch,一定要保證我們的hotpacth dex文件出現在dexElements列表的前面。

 

而要實現熱更新,就需要我們在運行時去更改PathClassLoader.pathList.dexElements,由於這些屬性都是private的,因此需要通過反射來修改。另外,構造我們自己的dex文件所對應的dexElements數組的時候,我們也可以采取一個比較取巧的方式,就是通過構造一個DexClassLoader對象來加載我們的dex文件,並且調用一次dexClassLoader.loadClass(dummyClassName);

 

比如dexClassLoader.pathList.dexElements中,就會包含我們的dex,通過把dexClassLoader.pathList.dexElements插入到系統默認的classLoader.pathList.dexElements列表前面,就可以讓系統優先加載我們的dex中的類,從而可以實現熱更新了。下面我們來看看其中的一部分代碼:

 

private static synchronized Boolean injectAboveEqualApiLevel14(

                 String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) {

      Log.i(TAG, "--> injectAboveEqualApiLevel14");

      PathClassLoader pathClassLoader = (PathClassLoader) DexInjector.class.getClassLoader();

      DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, pathClassLoader);

      try {

           dexClassLoader.loadClass(dummyClassName);

           Object dexElements = combineArray(

                      getDexElements(getPathList(pathClassLoader)),

                      getDexElements(getPathList(dexClassLoader)));

           Object pathList = getPathList(pathClassLoader);

           setField(pathList, pathList.getClass(), "dexElements", dexElements);

      } catch (Throwable e) {

           e.printStackTrace();

           return false;

      }

      Log.i(TAG, "<-- injectAboveEqualApiLevel14 End.");

      return true;

}

 

以上就是熱更新相關原理及實現代碼,希望能幫助大家更好的掌握這部分知識~~如果還有不清楚的地方,也可以直接去官網上面查看一下相關文檔。

 

相關文章:《Android UI設計的基本元素有哪些


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM