Android源码学习——ClassLoader(2)
来源:互联网 发布:炫佳网络 编辑:程序博客网 时间:2024/06/04 18:53
本文学习的源码参考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。
差不多就是这样了,因为没太看懂所以暂且先这么写写,后面再补充。
- Android源码学习——ClassLoader(2)
- Android源码学习——ClassLoader(1)
- Android 插件化基础——ClassLoader 源码解析
- tomcat源码阅读(三)——ClassLoader背景知识
- java源码学习5-ClassLoader
- 通过Tomcat源码学习ClassLoader
- Android ClassLoader之getSystemResourceAsStream源码分析
- tomcat源码阅读(二)——ClassLoader及catalina启动
- ClassLoader源码
- Android ClassLoader工作原理学习记录(一)
- Android ClassLoader工作原理学习记录(二)
- Android源码学习——linker(2)
- Android解析ClassLoader(二)Android中的ClassLoader
- 浅读Tomcat源码(五)---classLoader
- JVM学习笔记(1)——类加载器ClassLoader
- JVM学习笔记5—类加载器(classloader)
- classLoader学习
- ClassLoader学习
- Intelli idea 激活 license server
- [ubuntu 12] 命令行w3m上网工具安装和使用 2015年总结
- linux下使用cpu并发解压缩来加快速度
- Eureka Server安全认证
- 无法安装jenkins插件
- Android源码学习——ClassLoader(2)
- 【Selenium】4定位元素
- 注意pagerHelper插件的顺序
- C++数组与字符数组
- Spring学习(二)-Spring简介
- [ubuntu 12] 安装jdk 2015年总结
- android flowLayout
- shell完成闪瞎钛合金狗眼的进度条
- 每日一题(9)—— 写一个标准宏MIN,这个宏输入两个参数并返回较小的一个