Android源碼的食用方法
0x00 Prelude
有時(shí)候感覺發(fā)自內(nèi)心地喜歡Android侈贷。不僅是因?yàn)槲覀兛梢詣?chuàng)造「小而美」的應(yīng)用程序蓝厌,還因?yàn)镚oogle提供了很好的售后服務(wù)。
前兩天了解了一些關(guān)于ClassLoader的東西宙彪,我想真正的學(xué)習(xí)模式應(yīng)該是在用到的時(shí)候再去查找憔披,比如公司需要真正開發(fā)一套插件框架等限,我再去學(xué)習(xí),這樣的動(dòng)機(jī)下可能動(dòng)力會(huì)更足》蚁ィ現(xiàn)在是處于學(xué)習(xí)階段望门,雖然不知道看了有什么用,但還是去看吧锰霜。
Android源碼很龐大筹误,要完整看完是不太可能,我想起高中數(shù)學(xué)老師常常教育我們的一句話:「不要想著一口吃成個(gè)胖子癣缅〕簦」所以可以慢慢來。好在我們可以站在巨人的肩膀上友存,已經(jīng)有很多人寫了源碼的閱讀筆記祷膳,比如羅升陽的博客和書。
0x01 怎么找到具體的類
ClassLoader.java可以在Android Studio的android.jar中看到屡立,只要按住CMD點(diǎn)擊左鍵就可以跳轉(zhuǎn)了直晨,這是因?yàn)锳ndroid Studio下載sdk的時(shí)候attach了源碼到android.jar上。但是DexClassLoade.java的源碼打開的時(shí)候侠驯,看到的就是反編譯出來的代碼了抡秆;
這是因?yàn)閍ndroid.jar只會(huì)attach到一些常用的源碼,DexClassLoader.java這個(gè)類吟策,是在Dalvik目錄下的儒士,也許不是很常用,所以我們看到的是反編譯出來的源碼檩坚。反編譯出來的源碼沒有注釋着撩,可讀性也差很多诅福。比如,我們看到的是這樣的:
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
throw new RuntimeException("Stub!");
}
這個(gè)的意思是拖叙,函數(shù)具體的實(shí)現(xiàn)都放在ROM中了氓润,不在這里暴露。
我們可以去谷歌AOSP的網(wǎng)站上去看源碼薯鳍。比如android.jar咖气,它的內(nèi)容基本都在https://android.googlesource.com/platform/frameworks/base/+/android-6.0.1_r21/core/java/android/這個(gè)路徑下。但是內(nèi)容太多挖滤,找了一段時(shí)間也沒找到BaseDexClassLoader.java的位置崩溪,怎么辦?
這時(shí)候可以借助Google斩松,直接搜索:
where is BaseDexClassLoader.java,可以看到伶唯,貼心的Google第一條給出了API的地址,第二條給出了BaseDexClassLoader在AOSP中的地址惧盹。
0x02 插件的加載
插件裝載的執(zhí)行順序是:
BaseDexClassLoader--->pathList.findClass(name)--->loadClassBinaryName()乳幸,最終指向了一個(gè)native方法loadClassBinaryName()。
/**
* Finds the named class in one of the dex files pointed at by
* this instance. This will find the one in the earliest listed
* path element. If the class is found but has not yet been
* defined, then this method will define it in the defining
* context that this instance was constructed with.
*
* @return the named class or {@code null} if the class is not
* found in any of the dex files
*/
public Class findClass(String name) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext);
if (clazz != null) {
return clazz;
}
}
}
return null;
}
好了钧椰,明天我們看看Robile中的Plugin為什么重寫了loadClass()方法粹断。
-NOV23