美文网首页
ClassLoader--02基于Android5.0的open

ClassLoader--02基于Android5.0的open

作者: 冉桓彬 | 来源:发表于2019-04-22 22:23 被阅读0次

    相关源码地址:
    http://aospxref.com/android-5.0.1_r1/xref/art/runtime/native/dalvik_system_DexFile.cc
    http://aospxref.com/android-5.0.1_r1/xref/art/runtime/class_linker.cc
    http://aospxref.com/android-5.0.1_r1/xref/art/runtime/dex_file.cc

    分析涉及到几个主要的方法:

    1、dalvik_system_DexFile.DexFile_openDexFileNative
    2、class_linker.OpenDexFilesFromOat
    3、class_linker.FindOpenedOatDexFile
    4、class_linker.LoadMultiDexFilesFromOatFile
    5、class_linker.CreateOatFileForDexLocation
    6、oat_file.OpenDexFile
    7、dex_file.Open
    8、dex_file.OpenZip
    9、dex_file.OpenFromZip

    通过对Java层的源码分析, 其实并没有得到很有用的信息, 针对DexClassLoader与PathClassLoader的区别还是比较模糊的, 所以这篇打算从native层进行分析, 尝试搞清楚DexClassLoader与PathClassLoader区别的本质.

    一、java层

    1.1 PathClassLoader与DexClassLoader初始化
    // 1. 注意PathClassLoader与DexClassLoader不同点仅在于初始化时传给BaseDexClassLoader时是否支持optimizedDirectory;
    // 2. 正如DexClassLoader的类注释描述, DexClassLoader支持加载外部jar、apk中的dex中的类, 这个特殊点正是因为optimizedDirectory;
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
    public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
    // 一个类加载器,用于加载包含{@code classes.dex}条目的{@code .jar}和{@code .apk}文件中的类。
    // 这可用于执行未作为应用程序的一部分安装的代码。
    // 此类加载器需要一个应用程序专用的可写目录来缓存优化的类。使用{@code Context.getCodeCacheDir()}来创建
    // 这样的目录:{@ code File dexOutputDir = context.getCodeCacheDir();}
    // 不要在外部存储上缓存优化的类, 外部存储不提供保护应用程序免受代码注入攻击所必需的访问控制。
    public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
    
    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
    
    public class BaseDexClassLoader extends ClassLoader {
        private final DexPathList pathList;
        public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    }
    
    1.2 DexPathList初始化
    public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
        this.definingContext = definingContext;
        // splitDexPath不分析, 就是常规切割, 继续分析makeDexElements;
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    }
    
    1.3 DexPathList.makeDexElements
    private static final String DEX_SUFFIX = ".dex";
    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory) {
        ArrayList<Element> elements = new ArrayList<Element>();
        /*
         * Open all files and load the (direct or contained) dex files
         * up front.
         */
        for (File file : files) {
            File zip = null;
            DexFile dex = null;
            String name = file.getName();
            if (file.isDirectory()) {
                // We support directories for looking up resources.
                // This is only useful for running libcore tests.
                elements.add(new Element(file, true, null, null));
            } else if (file.isFile()){
                // 这里一定要注意哈, 当前的file可能是.dex、 .jar、 .apk三种类型;
                if (name.endsWith(DEX_SUFFIX)) {
                    // 当前文件是.dex文件
                    // Raw dex file (not inside a zip/jar).
                    dex = loadDexFile(file, optimizedDirectory);
                } else {// 当前文件是.jar或者是.apk文件
                    zip = file;
                    dex = loadDexFile(file, optimizedDirectory);
                }
            } 
            if ((zip != null) || (dex != null)) {
                elements.add(new Element(file, false, zip, dex));
            }
        }
        return elements.toArray(new Element[elements.size()]);
    }
    
    1.4 DexPathList.loadDexFile
    private static DexFile loadDexFile(File file, File optimizedDirectory) {
        if (optimizedDirectory == null) {
            // 如果optimizedDirectory为null, 则直接返回DexFile();
            return new DexFile(file);
        } else {
            // optimizedPath = new File(optimizedDirectory, file.getName()).getPath();
            // 注意optimizedPath是指优化后的dex文件存放的路径;
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }
    
    private static String optimizedPathFor(File path, File optimizedDirectory) {
        // 我们不想使用“.odex”,因为构建系统将其用于与仅资源jar文件配对的文件。如果VM可以假设匹配的jar中没有classes.dex,
        // 则不需要打开jar来检查更新的依赖项,从而在启动时提供轻微的性能提升。这里使用“.dex”匹配对/ data / dalvik-cache
        // 中文件的使用。
        String fileName = path.getName();
        File result = new File(optimizedDirectory, fileName);
        return result.getPath();
    }
    
    1.5 DexFile.loadDex
    // loadDex最终也是调用了DexFile();
    static public DexFile loadDex(String sourcePathName, String outputPathName, int flags) {
        // 我们可能想要缓存先前打开的DexFile对象。缓存将与close()同步.
        // 当应用程序决定多次打开它时,这将有助于我们避免多次映射相同的DEX.
        return new DexFile(sourcePathName, outputPathName, flags);
    }
    
    1.6 DexFile初始化
    // 触发native层的加载;
    private DexFile(String sourceName, String outputName, int flags) throws IOException {
        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        guard.open("close");
    }
    
    private static long openDexFile(String sourceName, String outputName, int flags) 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);
    }
    // sourceName: 对应原始jar/apk中的.dex;
    // outputName: dex文件优化后存放的地方;
    private static native long openDexFileNative(String sourceName, String outputName, int flags);
    

    二、native层dex文件加载

    2.1 dalvik_system_DexFile.DexFile_openDexFileNative
    // javaSourceName: 源.jar/.apk中的.dex
    // javaOutputName: 对应Java层的optimizedDirectory;
    static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
        ScopedUtfChars sourceName(env, javaSourceName);
    
        NullableScopedUtfChars outputName(env, javaOutputName);
        ClassLinker* linker = Runtime::Current()->GetClassLinker();
        // 创建DexFile的集合, 用于存放获取的DexFile对象;
        std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>());
        std::vector<std::string> error_msgs;
        // 从oat中加载优化后的.dex文件;
        bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs, dex_files.get());
        return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_files.release()));
    }
    
    2.2 class_linker.OpenDexFilesFromOat
    // dex_location: 对应sourceName, 可能是.dex、 .jar、 .apk其中的一种;
    // oat_location: 对应outputName, 对应Java层的optimizedDirectory;
    bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
                                          std::vector<std::string>* error_msgs,
                                          std::vector<const DexFile*>* dex_files) {
        // 在进行dex优化之前, 需要做三件事:
        // (1) 内存缓存: 首先检查我们是否已经打开了对应的oat文件, 这里可以理解为内存缓存, 在内存中是否有这样的一份缓存;
        // (2) 磁盘缓存: 如果没有打开过对应的oat文件, 再次判断是否已经做过dex优化, 并且在磁盘中缓存了对应的oat文件,
        // (3) 如果磁盘中存在,              
        // 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;  
        // (1) 首先校验目标oat文件是否存在, 也就是说与javaSourceName和javaOutputName对应的目标oat文件
        //     是否已经被加载过, 如果被加载 过, 则直接从内存缓存中获取目OatFile对象.
        const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
                                                                       dex_location_checksum_pointer);
        // 尝试通过OatDexFile获取目标OatFile并为OatFile* open_oat_file赋值, 接下来以open_oat_file为
        // 线索进行分析.
        std::unique_ptr<const OatFile> open_oat_file(
            oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);  
        // 这里开始第二步, 如果没有打开过对应的oat文件, 又分两种情况, 一种是磁盘中有, 也就是进行过dex优化,
        // 另一种是磁盘中没有, 也就是没有进行过dex优化.
        if (open_oat_file.get() == nullptr) {
            if (oat_location != nullptr) {// 这里是第一种情况, 进行过dex优化操作.
                //... 
            } else {// 这里是第二种情况, 未进行过dex优化操作.
                //...
            }   
            // 标志位, 如果没有缓存过目标OatFile, 此时将标志位置为true, 后续创建OatFile对象之后
            // 根据该标志位确认是否需要进行缓存.
            needs_registering = true;
        }  
        // (3) 尝试从磁盘中找到目标OatFile对象.
        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) {
                // 将目标oat_file进行注册, 以便于下次使用
                RegisterOatFile(oat_file);
            }
            return oat_file->IsExecutable();
        } else {
            ...
        }
        // (4) 如果没有对应的oat文件或者不匹配, 则需要进行重新生成和加载.
        std::string cache_location;
        // oat_location对应java层的optimizedDirectory.
        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 = null, 我们使用android系统默认路径: /data/dalvik-cache为oat默认路径;
            oat_location = cache_location.c_str();
        }
        bool has_flock = true;
    
        if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
            // 创建oat文件
            open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
                                                            oat_location, error_msgs));
        }
        // 再次尝试从OatFile中获取目标DexFile
        success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
                                               dex_location_checksum_pointer,
                                               true, error_msgs, dex_files);
        if (success) {
            // 如果获取成功, 缓存该OatFile
            RegisterOatFile(open_oat_file.release());
            return true;
        } else {
            return false;
        }
    }
    
    2.3 class_linker.FindOpenedOatDexFile
    // 从oat_files_查找是否存在对应的OatFile, 查找过程知道, OatFile缓存了oat_location;
    // oat_location: 对应java层的optimizedDirectory
    const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location,
                                                                 const char* dex_location,
                                                                 const uint32_t* dex_location_checksum) {
        ReaderMutexLock mu(Thread::Current(), dex_lock_);
        for (const OatFile* oat_file : oat_files_) {
            DCHECK(oat_file != nullptr);
            if (oat_location != nullptr) {
                if (oat_file->GetLocation() != oat_location) {
                    continue;
                }
            }
            // 如果oat_files_不存在与oat_location对应的OatFile, 是不会执行到这里的;
            // 如果存在与oat_location对应的OatFile, 根据OatFile获取oat_dex_file;
            const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
                                                                              dex_location_checksum,
                                                                              false);
            if (oat_dex_file != nullptr) {
                return oat_dex_file;
            }
        }
        return nullptr;
    }
    

    如果存在已经打开的oat文件, 这里会遍历所有打开的oat文件, 查找目标oat_file, 如果存在, 则返回该OatDexFile对象.
    这里还有很关键的一个点: 就是我们在加载Class时是从优化后的Dex中加载, 源码这里的顺序是先判断是否存在目标OatDexFile, 而OatDexFile与oat_location也就是java层的optimizedDirectory是一一对应的关系, 再结合模块<2.2L59~L65>对oat_location的赋值, 大致明白DexClassLoader与PathClassLoader的本质区别, DexClassLoader不支持传入optimizedDirectory, 所以被优化后的dex文件都存放在固定目录下, 如果使用DexClassLoader加载也只能加载固定目录下的dex文件, 而PathClassLoader支持optimizedDirectory, 被优化后的dex文件存放在optimizedDirectory路径下, 所以支持加载外部dex文件.

    2.4 class_linker.LoadMultiDexFilesFromOatFile
    static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
                                             const char* dex_location,
                                             const uint32_t* dex_location_checksum,
                                             bool generated,
                                             std::vector<std::string>* error_msgs,
                                             std::vector<const DexFile*>* dex_files) {
        size_t old_size = dex_files->size();  // To rollback on error.
        bool success = true;
        for (size_t i = 0; success; ++i) {
            // 获取目标OatDexFile对象.
            const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
            if (oat_dex_file == nullptr) {
                break;  // Not found, done.
            }
            // Checksum test. Test must succeed when generated.
            success = !generated;
            if (next_location_checksum_pointer != nullptr) {
                success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
            }
            if (success) {
                // 获取目标DexFile对象
                const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
                if (dex_file == nullptr) {
                    success = false;
                    error_msgs->push_back(error_msg);
                } else {
                    // 如果目标DexFile存在, 则将该DexFile进行压栈操作, 在dex_files中缓存起来.
                    dex_files->push_back(dex_file);
                }
            }
        }
        if (dex_files->size() == old_size) {
            success = false;  // We did not even find classes.dex
        }
        return success;
    }
    

    如果获取到目标DexFile, 将标志位置为true, 同时将DexFile进行压栈操作, 下次直接从栈获取即可, 如果没有获取到目标DexFile, 标志位置为false.

    2.5 class_linker.CreateOatFileForDexLocation
    const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location,
                                                            int fd, const char* oat_location,
                                                            std::vector<std::string>* error_msgs) {
        // Generate the output oat file for the dex file
        VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
        std::string error_msg;
        // 创建.dex对应的oat文件, 如何创建的先不分析<TODO看完老罗的文章之后再做分析>
        if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) {
            CHECK(!error_msg.empty());
            error_msgs->push_back(error_msg);
            return nullptr;
        }
        // 创建完成之后再打开该文件并创建OatFile实例;
        std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
                                                !Runtime::Current()->IsCompiler(),
                                                &error_msg));
        if (oat_file.get() == nullptr) {
            std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
                                                    oat_location, error_msg.c_str());
            error_msgs->push_back(compound_msg);
            return nullptr;
        }
        return oat_file.release();
    }
    
    2.6 oat_file.OpenDexFile
    const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
        return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
                             dex_file_location_checksum_, error_msg);
    }
    
    2.7 dex_file.Open
    bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
                       std::vector<const DexFile*>* dex_files) {
        uint32_t magic;
        ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
        // 如果filename是.zip格式, 即java层对应的.jar或者.apk;
        if (IsZipMagic(magic)) {
            // 从压缩包中获取.dex文件, 结合下文对OpenZip的分析可知, 将zip中的.dex文件放入dex_files中;
            return DexFile::OpenZip(fd.release(), location, error_msg, dex_files);
        }
        // 如果filename是.dex格式;
        if (IsDexMagic(magic)) {
            // 则直接加载dex文件;
            std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true, error_msg));
            if (dex_file.get() != nullptr) {
                dex_files->push_back(dex_file.release());
                return true;
            } else {
                return false;
            }
        }
        *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
        return false;
    }
    
    2.8 dex_file.OpenZip
    bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
                          std::vector<const  DexFile*>* dex_files) {
        std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
        return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
    }
    
    
    2.9 dex_file.OpenFromZip
    bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
                              std::string* error_msg, std::vector<const DexFile*>* dex_files) {
        ZipOpenErrorCode error_code;
        std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg, &error_code));
        if (dex_file.get() == nullptr) {
            return false;
        } else {
            // Had at least classes.dex.
            dex_files->push_back(dex_file.release());
            // Now try some more.
            size_t i = 2;
            // We could try to avoid std::string allocations by working on a char array directly. As we
            // do not expect a lot of iterations, this seems too involved and brittle.
            while (i < 100) {
                std::string name = StringPrintf("classes%zu.dex", i);
                std::string fake_location = location + kMultiDexSeparator + name;
                std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location,
                                                             error_msg, &error_code));
                if (next_dex_file.get() == nullptr) {
                    if (error_code != ZipOpenErrorCode::kEntryNotFound) {
                        LOG(WARNING) << error_msg;
                    }
                    break;
                } else {
                    // 遍历zip文件, 将.dex文件放入到dex_files集合中;
                    dex_files->push_back(next_dex_file.release());
                }
                i++;
            }
            return true;
        }
    }
    

    如果filename(对应java层的dexPath)对应的文件是一个.zip/.jar文件, 此时会对该包里面所有的dex文件进行dex优化操作, 这个知识点在对Multidex进行分析时会涉及到(为什么5.0以后不需要使用Multidex).

    2.10 class_linker.RegisterOatFile
    const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
        WriterMutexLock mu(Thread::Current(), dex_lock_);
        if (kIsDebugBuild) {
            for (size_t i = 0; i < oat_files_.size(); ++i) {
                CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation();
            }
        }
        VLOG(class_linker) << "Registering " << oat_file->GetLocation();
            // 缓存OatFile对象;
        oat_files_.push_back(oat_file);
        return oat_file;
    }
    

    相关文章

      网友评论

          本文标题:ClassLoader--02基于Android5.0的open

          本文链接:https://www.haomeiwen.com/subject/tmdriqtx.html