編輯:Android資訊
最近Android社區的氛圍很不錯嘛,連續放出一系列的Android動態加載插件和熱更新庫,這篇文章就來介紹一下Android中實現熱更新的原理。
我們知道Java在運行時加載對應的類是通過ClassLoader來實現的,ClassLoader本身是一個抽象來,Android中使用PathClassLoader類作為Android的默認的類加載器,
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; }
完整的demo請參考我的 GitHub
上面只是說了一下hotpatch的原理,具體實現的時候,如果你的app應用了multidex,還會遇到其他的坑,請參考:
為什麼要自定義控件 有時,原生控件不能滿足我們對於外觀和功能的需求,這時候可以自定義控件來定制外觀或功能;有時,原生控件可以通過復雜的編碼實現想要的功能,這時候可
我們先假設一個場景需求:剛有孩子的爸爸媽媽對用照片、視頻記錄寶寶成長有強烈的意願,但苦於目前沒有一款專門的手機APP做這件事。A公司洞察到市場需求,要求開發團隊盡
關於Android View控件 Android中控件大致被分為兩類ViewGroup,View。ViewGroup作為容器管理View。Android視圖,是類
序言 之前寫過一篇關於Android項目如何架構的,有MVC和MCVP,前幾天又看到了新的一種架構,當然並不是新出的,出了有一段時間,當前被應用的並不是很普遍,接