本文学习的源码参考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。
差不多就是这样了,因为没太看懂所以暂且先这么写写,后面再补充。