Android開發過程當中,開發的小夥伴對動態加載代碼確定不陌生。使用各個開源框架的中都應該有接觸,其主要原理離不開ClassLoader等相關的類。這裏咱們會從Android中ClassLoader等相關類的源碼入手,更好的理解和學習動態加載類的原理。java
ClassLoader 的繼承關係以下:c++
這裏咱們主要分析一下 BaseDexClassLoader.findClass()
和 ClassLoader.loadClass()
兩個函數在系統中是怎麼進行查找class的過程。數組
咱們看一下系統加載類ClassLoader.loadClass()
函數實現代碼,在ClassLoader.java
中:緩存
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先 檢測是否已經加載過 Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //去調用父類的loadClass c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { //未找到的狀況下,使用findClass在當前dex查找 c = findClass(name); } } return c; } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
loadClass()
先調用findLoadedClass()
來判斷當前類是否已加載;BootClassLoader
中查找;findClass()
查找當前dex。下圖爲
findLoadedClass()
的調用流程;根據調用流程圖配合源代碼進行詳細的分析原理。微信
下面介紹對應的源代碼實現部分:cookie
protected final Class<?> findLoadedClass(String name) { ClassLoader loader; if (this == BootClassLoader.getInstance()) loader = null; else loader = this; return VMClassLoader.findLoadedClass(loader, name); }
函數最終統一調用VMClassLoader.findLoadedClass()
進行查找類。框架
native static Class findLoadedClass(ClassLoader cl, String name);
實如今java_lang_VMClassLoader.cc
文件中。ide
static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader,jstring javaName) { .... ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(javaLoader); ClassLinker* cl = Runtime::Current()->GetClassLinker(); ObjPtr<mirror::Class> c = VMClassLoader::LookupClass(cl, soa.Self(), descriptor.c_str(), descriptor_hash, loader); if (c != nullptr && c->IsResolved()) { return soa.AddLocalReference<jclass>(c); } ... if (loader != nullptr) { // Try the common case. StackHandleScope<1> hs(soa.Self()); c = VMClassLoader::FindClassInPathClassLoader(cl, soa, soa.Self(), descriptor.c_str(), descriptor_hash, hs.NewHandle(loader)); if (c != nullptr) { return soa.AddLocalReference<jclass>(c); } } return nullptr; } static mirror::Class* LookupClass(ClassLinker* cl, Thread* self, const char* descriptor, size_t hash, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { return cl->LookupClass(self, descriptor, hash, class_loader); } static ObjPtr<mirror::Class> FindClassInPathClassLoader(ClassLinker* cl, ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr<mirror::Class> result; if (cl->FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result)) { return result; } return nullptr; }
上述代碼findLoadedClass()
分爲兩步;函數
class_linker_->Lookupclass()
進行查找加載類;class_linker_->FindClassInPathClassLoader()
進行查找。
class_linker_
在虛擬機的啓動startVM()
函數的時候進行的初始化。<br>Runtime::class_linker_
在Runtime::Init()
函數的時候作的初始化。學習
if (UNLIKELY(IsAotCompiler())) { class_linker_ = new AotClassLinker(intern_table_); } else { class_linker_ = new ClassLinker(intern_table_); }
繼續來分析ClassLinker::LookupClass()
函數的具體實現;
mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, ObjPtr<mirror::ClassLoader> class_loader) { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); ClassTable* const class_table = ClassTableForClassLoader(class_loader); if (class_table != nullptr) { ObjPtr<mirror::Class> result = class_table->Lookup(descriptor, hash); if (result != nullptr) { return result.Ptr(); } } return nullptr; }
LookupClass()
函數經過class_loader
是否爲nullptr
,nullptr
使用boot_class_table_
來獲取class_table
, 不然獲取當前ClassLoader
的ClassTable
。 class_table
存放當前已經加載過的class,其實能夠理解爲class cache。如何進行dex 解析和aot等加載系統類和解析映射到內存中的不在此處展開分析。能夠了解art虛擬機啓動進行詳細分析。
下圖是findClass的調用流程;根據調用流程圖配合下面的代碼進行詳細的分析瞭解;
下面咱們介紹對應的源代碼實現部分。
findClass()
函數在BaseDexClassLoader.java
實現, 該函數主要作的事情就是在當前dex中查找類。若是類在當前dex中即返回。
代碼以下:
@Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ... throw cnfe; } return c; }
pathList
類型爲DexPathList
用來保存dexfile
文件的句柄等dex的操做。pathList.findClass()
實如今當前dex中查找類, pathList
在new DexClassLoader()
構造時初始化。
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) { ... this.pathList = new DexPathList(this, dexPath, librarySearchPath, null); ... }
DexPathList.java
public DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory) { ... this.definingContext = definingContext; ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); // save dexPath for BaseDexClassLoader this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext); this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories); if (suppressedExceptions.size() > 0) { this.dexElementsSuppressedExceptions = suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]); } else { dexElementsSuppressedExceptions = null; } }
dexElements
數組保存dexfile文件句柄。具體實如今makeDexElements()
函數中調用loadDexFile()
函數加載dex。該函數實現:
DexFile.java private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements) throws IOException { if (optimizedDirectory == null) { return new DexFile(file, loader, elements); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements); } }
DexFile.loadDex()
進行解析加載dex文件。關鍵代碼以下:
private DexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { ... mCookie = openDexFile(sourceName, outputName, flags, loader, elements); mInternalCookie = mCookie; mFileName = sourceName; ... } private static Object openDexFile(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { // Use absolute paths to enable the use of relative paths when testing on host. return openDexFileNative(new File(sourceName).getAbsolutePath(), (outputName == null) ? null : new File(outputName).getAbsolutePath(), flags,loader,elements); } private static native Object openDexFileNative(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements);
最終打開dexfile
是經過native
方法實現,而且返回mCookie
, mCookie
類型是int
用來標識dex
的惟一性。 openDexFileNative()
實現代碼:
//`dalvik_system_DexFile.cc` static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint flags ATTRIBUTE_UNUSED, jobject class_loader, jobjectArray dex_elements) { ... Runtime* const runtime = Runtime::Current(); ClassLinker* linker = runtime->GetClassLinker(); ... dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), class_loader, dex_elements, /*out*/ &oat_file, /*out*/ &error_msgs); .... }
上述代碼經過aotManager
打開並返回mCookie
,進一步的打開實現不在此處展開。即上述已經已經填充elements[]
,下面開始展開pathList.findClass()
函數的查找方式。
//BaseDexClassLoader.java public Class<?> findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { Class<?> clazz = element.findClass(name, definingContext, suppressed); if (clazz != null) { return clazz; } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; }
findClass()
會遍歷elements[]
, 每一個element
保存了dex的DexFile
句柄,而後調用loadClassBinaryName()
函數進行當前dex查找類。
//DexPathList.java public Class<?> findClass(String name, ClassLoader definingContext, List<Throwable> suppressed) { return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed): null; }
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, this, suppressed); } private static Class defineClass(String name, ClassLoader loader, Object cookie, DexFile dexFile, List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie, dexFile); } catch (NoClassDefFoundError e) { if (suppressed != null) { suppressed.add(e); } } catch (ClassNotFoundException e) { if (suppressed != null) { suppressed.add(e); } } return result; }
真正去dex或者內存中查找類的函數在native
中defineClassNative()
實現, 咱們來分析一下真正的實現過程:
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile) //dalvik_system_DexFile.cc static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jobject cookie, jobject dexFile) { std::vector<const DexFile*> dex_files; const OatFile* oat_file; if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) { ... return nullptr; } ScopedUtfChars class_name(env, javaName); ... const std::string descriptor(DotToDescriptor(class_name.c_str())); const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str())); for (auto& dex_file : dex_files) { ... ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash, class_loader, *dex_file, *dex_class_def); // Add the used dex file. This only required for the DexFile.loadClass API since normal // class loaders already keep their dex files live. class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile), class_loader.Get()); .... return soa.AddLocalReference<jclass>(result); } } ... return nullptr; }
經過Runtime
拿到當前的ClassLinker
對象,而後經過class_linker->DefineClass()
在當前dex中進行查找類。而後把找到的類經過class_linker->InsertDexFileInToClassLoader()
插入到class_table中進行緩存,返回查找到的類。這裏不進一步展開分析。
Android ClassLoader加載過程的源代碼分析到此已經分析的差很少了,若是想深刻的瞭解具體原理,能夠本身看源代碼的實現。 這裏就介紹到這裏。初次寫技術分享的文章,若有錯誤請指正,感謝!
<br>
(360技術原創內容,轉載請務必保留文末二維碼,謝謝~)
關於360技術
360技術是360技術團隊打造的技術分享公衆號,天天推送技術乾貨內容
更多技術信息歡迎關注「360技術」微信公衆號