Android平台类加载流程源码分析
前言
其實大家都知道的Android是使用Java作為開發語言,但是他使用的虛擬機卻并不是傳統的JVM,在4.4以前Android使用Dalvik虛擬機,之后使用ART(Android Runtime).
Dalvik和ART與傳統的JVM不同的地方在于,為了更加高效的在移動終端運行,Google重新定義了一套Dalvik字節碼,用于在Dalvik和ART虛擬機上運行.
因此如果你希望自己在本地生成的Java代碼能夠在Android機器上運行,就必須通過轉碼的形式生成一份Dex文件.
生成Dex文件
當我們完成Java代碼的書寫后,需要使用dx命令將Java代碼由javac生成的class文件編譯為Android虛擬機可識別的dex文件.
編譯命令如下
1 dx --dex --output 'dex' 'target'其中dex代表將要輸出的dex文件名,target代表用于作為輸入的文件,可以為class或jar文件.
解壓生成的dex文件后,你會發現一個叫做classes.dex的文件,是不是很眼熟? 在每個Android的APK中都會存在這么一個文件,他才是所有代碼的集合,類似于class文件.
生成完了Dex文件,接下來,讓我們看看如何在Android設備上運行它.
加載Dex文件
下面是一個Dex文件加載的Demo:
1 Context context = getBaseContext(); 2 DexClassLoader loader = new DexClassLoader("/sdcard/demo.jar", 3 context.getCacheDir().getAbsolutePath(), null, context.getClassLoader()); 4 try { 5 Class<?> clazz = loader.loadClass("com.kifile.sample.Sample"); 6 Object object = clazz.newInstance(); 7 Method method = clazz.getDeclaredMethod("println"); 8 if (method != null) { 9 method.invoke(object); 10 } 11 } catch (ClassNotFoundException e) { 12 e.printStackTrace(); 13 } catch (InstantiationException e) { 14 e.printStackTrace(); 15 } catch (IllegalAccessException e) { 16 e.printStackTrace(); 17 } catch (NoSuchMethodException e) { 18 e.printStackTrace(); 19 } catch (InvocationTargetException e) { 20 e.printStackTrace(); 21 }通過以上代碼,能夠從位于/sdcard/demo.jar的文件中讀取出一個”com.kifile.sample.Sample”類,并生成實例,動態調用其內部的”println”方法.
類加載流程分析
說了這么多了,都是教大家怎么在Android下動態加載java文件,現在我們來深入了解一下Android的類加載流程.
傳統Java類加載流程
首先對于我們先簡單說一下傳統的Java程序的類加載流程.
幾乎每一個Java類進行加載的時候都是通過ClassLoader進行加載的,當涉及引用新的類時,系統會自動調用當前類的ClassLoader區加載新類.
在加載的時候,首先判斷類有無加載,然后再從父ClassLoader中嘗試加載Class(之所以優先從父ClassLoader加載,是為了方便其他的兄弟ClassLoader能夠復用這個Class),當父ClassLoader也無法找到時,才通過findClass方法進行自查找.
Dex類加載流程
熟悉了這個之后,我們再來看看Android的類加載流程.
其實兩者的基本流程都是類似的:在能夠復用類的時候,盡量復用類,實在找不到Class,才自己去查找,但Android程序畢竟同普通的Java程序有所不同,
首先Android程序里所有組件的啟動均在ActivityThread中執行,同時在ActivityThread中會擁有一個LoadedApk的對象,在LoadedApk對象中,保存了包相關的數據,例如Dex文件和資源文件存放地址.
當我們啟動Application,Activity,Service等組件的時候,系統從LoadedApk中獲取ClassLoader對象,然后通過loadClass的方式,加載類.
這個ClassLoader對象就是PathClassLoader,因此對于Android應用而言,他幾乎所有的類都是通過PathClassLoader進行加載(除了諸如String,System等類,他們在Dalvik虛擬機創建時就注冊了).
所以我們需要關心的就是PathClassLoader的加載流程,PathClassLoader與上面樣例代碼中的DexClassLoader一樣繼承自BaseDexClassLoader.
我們看看BaseDexClassLoader類加載器的加載流程圖.
以上文件的源碼路徑分別為:
$ANDROID/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java $ANDROID/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java $ANDROID/libcore/dalvik/src/main/java/dalvik/system/DexFile.java $ANDROID/art/runtime/native/dalvik_system_DexFile.cc $ANDROID/art/runtime/native/java_lang_VMClassLoader.cc
如上所示,當DexClassLoader需要加載類時,先通過VMClassLoader查詢該類是否已經加載,如果尚未加載就通過創建時傳入的dexPath地址獲取到dex包路徑,然后根據Dex包路徑去實例化Class對象,并存入虛擬機中,然后返回.
當你成功的從ClassLoader獲取到Class時,其實在dalvik_system_DexFile中已經將當前類放置到ClassLoader對應的一張Hash表中,便于你下次重新獲取:
1 #$ANDROID/art/runtime/native/dalvik_system_DexFile.cc#DexFile_defineClassNative 2 class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object*>(dexFile), 3 class_loader.Get()); 1 #$ANDROID/art/runtime/class_linker.cc#InsertDexFileInToClassLoader 2 class_table->InsertWithHash(klass, hash);當我們通過ClassLoader獲取類時,通過以下代碼,將ClassLoader本身傳入本地代碼塊,從ClassLoader對應的Hash表中查詢類是否已經定義.
1 protected final Class<?> findLoadedClass(String className) { 2 ClassLoader loader; 3 if (this == BootClassLoader.getInstance()) 4 loader = null; 5 else 6 loader = this; 7 return VMClassLoader.findLoadedClass(loader, className); 8 }以此,Android就完成了他的類加載流程.
原文地址:?http://blog.kifile.com/android/2015/11/10/dex_class_loader.html
總結
以上是生活随笔為你收集整理的Android平台类加载流程源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详解Linux-I2C驱动
- 下一篇: Android客户端内置内存工具进行崩溃