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。

差不多就是这样了,因为没太看懂所以暂且先这么写写,后面再补充。