本文學習的源碼參考AndroidXRef,版本爲Lollipop 5.1.0_r1。
上一篇文章分析到兩個類加載器PathClassLoader和DexClassLoader最後都通過一個native方法openDexFileNative來實現DexFile對象的構造。
我們看下這個native方法的實現:
static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
ScopedUtfChars sourceName(env, javaSourceName);
if (sourceName.c_str() == NULL) {
return 0;
}
NullableScopedUtfChars outputName(env, javaOutputName);
if (env->ExceptionCheck()) {
return 0;
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>());
std::vector<std::string> error_msgs;
bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
dex_files.get());
if (success || !dex_files->empty()) {
// In the case of non-success, we have not found or could not generate the oat file.
// But we may still have found a dex file that we can use.
return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_files.release()));
} else {
// The vector should be empty after a failed loading attempt.
DCHECK_EQ(0U, dex_files->size());
ScopedObjectAccess soa(env);
CHECK(!error_msgs.empty());
// The most important message is at the end. So set up nesting by going forward, which will
// wrap the existing exception as a cause for the following one.
auto it = error_msgs.begin();
auto itEnd = error_msgs.end();
for ( ; it != itEnd; ++it) {
ThrowWrappedIOException("%s", it->c_str());
}
return 0;
}
}
這個函數的參數有五個,我們關注最後三個,就是Java層傳下來的三個參數。javaSourceName是源文件路徑,也即dex文件路徑,javaOutputName如果存在的話爲輸出路徑,最後是一個int型的flag。
這個函數首先做了Java到C++的類型轉化,javaSourceName賦給了dex_location,javaOutputName賦給了outputName。
然後調用GetClassLinker()
獲取當前運行時的ClassLinker,後面優化的時候要用。
然後創建了一個智能指針,指向DexFile的常量對象,還有錯誤信息的vector容器。
接着調用linker的OpenDexFilesFromOat
方法,獲取DexFile對象,返回一個是否成果的bool變量。如果成功並且dex_files不爲空,則返回dex_files,否則,執行ScopedObjectAccess
進行線程加鎖(???),然後對錯誤消息做個封裝並拋出異常。
看下OpenDexFilesFromOat的實現,有點長,一步步來:
第一部分,檢查是否存在打開的oat文件;
bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
std::vector<const DexFile*>* dex_files) {
// 1) Check whether we have an open oat file.
// This requires a dex checksum, use the "primary" one.
uint32_t dex_location_checksum;
uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
bool have_checksum = true;
std::string checksum_error_msg;
if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
// This happens for pre-opted files since the corresponding dex files are no longer on disk.
dex_location_checksum_pointer = nullptr;
have_checksum = false;
}
bool needs_registering = false;
const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
dex_location_checksum_pointer);
std::unique_ptr<const OatFile> open_oat_file(
oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
......
首先獲取dex_location下的dexfile的checksum,然後調用一個FindOpenedOatDexFile
獲得OatDexFile對象。
繼續看第二部分,如果不存在打開的oat文件,則從磁盤上找是否已存在oat文件:
// 2) If we do not have an open one, maybe there's one on disk already.
ScopedFlock scoped_flock;
if (open_oat_file.get() == nullptr) {
if (oat_location != nullptr) {
// Can only do this if we have a checksum, else error.
if (!have_checksum) {
error_msgs->push_back(checksum_error_msg);
return false;
}
std::string error_msg;
// We are loading or creating one in the future. Time to set up the file lock.
if (!scoped_flock.Init(oat_location, &error_msg)) {
error_msgs->push_back(error_msg);
return false;
}
// TODO Caller specifically asks for this oat_location. We should honor it. Probably?
open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
oat_location, &error_msg));
if (open_oat_file.get() == nullptr) {
std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
dex_location, oat_location, error_msg.c_str());
VLOG(class_linker) << compound_msg;
error_msgs->push_back(compound_msg);
}
} else {
// TODO: What to lock here?
bool obsolete_file_cleanup_failed;
open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
dex_location_checksum_pointer,
kRuntimeISA, error_msgs,
&obsolete_file_cleanup_failed));
// There's no point in going forward and eventually try to regenerate the
// file if we couldn't remove the obsolete one. Mostly likely we will fail
// with the same error when trying to write the new file.
// TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
if (obsolete_file_cleanup_failed) {
return false;
}
}
needs_registering = true;
}
這裏會開啓一個鎖,以防不同進程同時進行加載、註冊或生成文件導致衝突。
如果open_oat_file爲空,首先去檢查oat_location,不爲空時,啓動一個文件鎖,然後調用FindOatFileInOatLocationForDexFile
去重新設置open_oat_file,否則,調用FindOatFileContainingDexFileFromDexLocation
獲得oat文件然後重置open_oat_file。
主要就是分別在oat_location和dex_location兩個位置去找有沒有oat文件。
接下來看第三部分,檢查dex文件:
// 3) If we have an oat file, check all contained multidex files for our dex_location.
// Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
dex_location_checksum_pointer,
false, error_msgs, dex_files);
if (success) {
const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
if (needs_registering) {
// We opened the oat file, so we must register it.
RegisterOatFile(oat_file);
}
// If the file isn't executable we failed patchoat but did manage to get the dex files.
return oat_file->IsExecutable();
} else {
if (needs_registering) {
// We opened it, delete it.
open_oat_file.reset();
} else {
open_oat_file.release(); // Do not delete open oat files.
}
}
依次加載oat文件中的dex,並作checksum的校驗,所有工作完成後成功則將oat文件release之後進行註冊,否則進行reset或release。
最後看第四部分,如果上述條件都不滿足,重新生成oat文件並加載:
// 4) If it's not the case (either no oat file or mismatches), regenerate and load.
// Need a checksum, fail else.
if (!have_checksum) {
error_msgs->push_back(checksum_error_msg);
return false;
}
// Look in cache location if no oat_location is given.
std::string cache_location;
if (oat_location == nullptr) {
// Use the dalvik cache.
const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
oat_location = cache_location.c_str();
}
bool has_flock = true;
// Definitely need to lock now.
if (!scoped_flock.HasFile()) {
std::string error_msg;
if (!scoped_flock.Init(oat_location, &error_msg)) {
error_msgs->push_back(error_msg);
has_flock = false;
}
}
if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
// Create the oat file.
open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
oat_location, error_msgs));
}
// Failed, bail.
if (open_oat_file.get() == nullptr) {
std::string error_msg;
// dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
DexFile::Open(dex_location, dex_location, &error_msg, dex_files);
error_msgs->push_back(error_msg);
return false;
}
// Try to load again, but stronger checks.
success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
dex_location_checksum_pointer,
true, error_msgs, dex_files);
if (success) {
RegisterOatFile(open_oat_file.release());
return true;
} else {
return false;
}
}
首先獲取oat_location的地址,如果不存在則使用dalvik_cache的地址,然後設置鎖,檢查是否可以進行dex2oat,接着去生成oat文件並重新加載dex。
差不多就是這樣了,因爲沒太看懂所以暫且先這麼寫寫,後面再補充。