Android Studio升級到2.0以後,新增了Instant Run功能,該功能能夠熱替換apk中的部分代碼,大幅提升測試安裝的效率。android
可是,因爲個人項目中自定義了一些ClassLoader,當使用InstantRun時,常常出現class加載不正確的問題。分析後緣由以下。json
使用Instant Run編譯出的apk裏面會多出幾個dex文件,和一個instant-run.zip,這個zip裏也是一堆dex文件:app
因此推測,instant Run的實現原理是:ide
根據代碼結構,將App的源碼分割成多個dex,而後使用自定義的classloader來加載他們,固然這個自定義的classloader也要繼承BaseDexClassLoader,由於BaseDexClassLoader裏有個DexPathList,這個所謂的List裏存的是多個dex的文件信息,因此當某段代碼修改時,只需編譯和替換相應的dex文件便可(這一樣也是MultiDex的實現原理)。測試
下面咱們簡單驗證一下。spa
首先正常的apk運行時,其classloader打印出來是這樣的:3d
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.zkw.hostdemo-1/base.apk"],nativeLibraryDirectories=[/data/app/com.zkw.hostdemo-1/lib/arm, /system/lib, /vendor/lib, system/vendor/lib, system/vendor/lib/egl, system/lib/hw]]]
而使用Instant Run運行起來的apk,其classloader打印出來長這樣:code
com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader[DexPathList[[dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-support-annotations-23.3.0_6be31c7c3de045eced09b0e58c45c46ba1a8b4da-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-ormlite-core-4.47_2aa30d6da8ed45bfc6255e592f80eb5f44eace4c-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-ormlite-android-4.47_a7ee90c985672a4cd4bdfca79190a330465fcdb0-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-lecore_4c1e07fda866033d90832ceae724962d4943e790-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-internal_impl-23.1.1_12c3206fb094d3315355dc809fcd39ad94f6e5e8-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-fastjson-1.1.45.android_317ce285230b187682809682934f3690b4d9580d-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-com.android.support-support-v4-23.1.1_1cfecee433501e7056d02c08b2393c569c575b33-classes.dex", dex file "/data/data/com.zkw.hostdemo/files/instant-run/dex/slice-com.android.support-multidex-1.0.1_a3117677a6ad7ee678ee3f3a4af434c4d08ee7ba-classes.dex"],nativeLibraryDirectories=[/data/app/com.zkw.hostdemo-1/lib/arm, /system/lib, /vendor/lib, system/vendor/lib, system/vendor/lib/egl, system/lib/hw]]]
很長吧,發個截圖:orm
能夠看到這個ClassLoader是:com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader,DexPathList對應着instant-run.zip裏的全部dex。blog
到這裏,感受基本符合個人猜測,可是這個IncrementalClassLoader是如何添加到設備中的,又如何在運行時起做用的,咱們接着分析。
instant run運行的apk裏,和class有關的就是classes.dex、classes2.dex和instant-run.zip裏的dex。
經過觀察命名和反編譯,我發現instant-run.zip裏的dex都是和咱們app代碼相關的,而classes.dex和classes2.dex是AndroidStudio編譯時本身添加的,其中classes2.dex最重要,咱們反編譯看看:
終於看到了熟悉的IncrementalClassLoader,那這個classloader是怎麼生效的呢,咱們看看BootstrapApplication的源碼(可想而知,AndroidManifest文件中的application屬性也被Android Studio改了):
注意標綠的那行,繼續進去看看(那行下面有個createRealApplication(),其實就是經過反射獲取app自定義的Application,而後作對應的操做):
灰色的那行就是安裝IncrementalClassLoader的地方,進入IncrementalClassLoader的inject()看看,
源碼中能夠看到,Studio其實就是利用反射,將本身的IncrementalClassLoader設置成app中默認ClassLoader的parent,這樣就攔截了全部類加載的動做,從而實現了對多個dex文件的動態加載。
到此,原理分析結束。
因此若是你的app裏用到了自定義的ClassLoader,請謹慎使用Instant Run!
謝謝閱讀,轉載請註明出處。