ART 加载dex文件

一.Java层调用链

1.1 ActivityThread.performLaunchActivity

一般应用程序的启动都是由点击Launcher上的图标来启动,而点击图标时所执行的操作是startActivity(Intent intent),经过一系列复杂的调用链后,最终会初始化Activity实例并回调onCreate,而执行初始化实例并回调onCreate的方法是ActivityThread.performLaunchActivity, 核心代码为:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  ...
  Activity activity = null;
  try {      
      //获取ClassLoader同时加载相应的dex文件
      //r.packageInfo是LoadApk的实例
      java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
      //初始化Activity实例
      activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
      StrictMode.incrementExpectedActivityCount(activity.getClass());
      r.intent.setExtrasClassLoader(cl);
      r.intent.prepareToEnterProcess();
      if (r.state != null) {
                r.state.setClassLoader(cl);
            }
  } catch (Exception e) {
      if (!mInstrumentation.onException(activity, e)) {
        throw new RuntimeException(
          "Unable to instantiate activity " + component
          + ": " + e.toString(), e);
      }
  }
...
}

1.2 LoadApk.getClassLoader

public ClassLoader getClassLoader() {
    synchronized (this) {
        //如果已经加载过dex文件则直接返回ClassLoader
        if (mClassLoader != null) {
            return mClassLoader;
        }
        ...
        //如果还没有加载dex,则初始化相应的ClassLoader并加载dex文件
        //mBaseClassLoader = null,在performLaunchActivity中初始化LoadApk的时候设置
        mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
                        mBaseClassLoader);
    }
    return mClassLoader;
}

1.3 ApplicationLoaders.getClassLoader

public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent){
    //Class.getSystemClassLoader返回的是一个PathClassLoader
    //baseParent是BootClassLoader
    ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();

    synchronized (mLoaders) {
    if (parent == null) {
        parent = baseParent;
    }

    if (parent == baseParent) {
        ClassLoader loader = mLoaders.get(zip);
        if (loader != null) {
            return loader;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
        //创建一个PathClassLoader,并放入缓存,方便以后直接获取
        PathClassLoader pathClassloader =
                    new PathClassLoader(zip, libPath, parent);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        mLoaders.put(zip, pathClassloader);
        return pathClassloader;
    }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
        PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        return pathClassloader;
    }
}

1.4 PathClassLoader()

public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
    //PathClassLoader继承自BaseDexClassLoader,所以会调用
    //BaseDexClassLoader的构造函数
    super(dexPath, null, libraryPath, parent);
}

1.5 BaseDexClassLoader()

//由PathClassLoader传入的参数可以知道第二个参数optimizedDirectory=null
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}

1.6 DexPathList()

public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {

    ...

    this.definingContext = definingContext;

    ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
    //加载dex文件
    this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,
                                            suppressedExceptions);

    this.nativeLibraryDirectories = splitPaths(libraryPath, false);
    this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
    List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
    allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
    //加载library文件
    this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null,
                                                          suppressedExceptions);

    ...

1.7 DexPathList.makePathElements()

private static Element[] makePathElements(List<File> files, File optimizedDirectory,
                                              List<IOException> suppressedExceptions) {
    List<Element> elements = new ArrayList<>();
    for (File file : files) {
        File zip = null;
        File dir = new File("");
        DexFile dex = null;
        String path = file.getPath();
        String name = file.getName();

        if (path.contains(zipSeparator)) {
            String split[] = path.split(zipSeparator, 2);
            zip = new File(split[0]);
            dir = new File(split[1]);
        } else if (file.isDirectory()) {
            //如果file是library会执行这一分支
            elements.add(new Element(file, true, null, null));
        } else if (file.isFile()) {
            if (name.endsWith(DEX_SUFFIX)) {
                //如果文件以.dex结尾,直接加载
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: " + file, ex);
                }
            } else {
                zip = file;
                //加载位于.zip或者.apk文件内的dex文件
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException suppressed) {
                    suppressedExceptions.add(suppressed);
                }
            }
        } else {
            System.logW("ClassLoader referenced unknown path: " + file);
        }

        if ((zip != null) || (dex != null)) {
            elements.add(new Element(dir, false, zip, dex));
        }
    }

    return elements.toArray(new Element[elements.size()]);
}

1.8 DexPathList.loadDexFile()

private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
    if (optimizedDirectory == null) {
        //如果是PathClassLoader,则执行这一步
        return new DexFile(file);
    } else {
        //首先确保输出后的文件是以.dex结尾
        String optimizedPath = optimizedPathFor(file, optimizedDirectory);
        return DexFile.loadDex(file.getPath(), optimizedPath, 0);
    }
}

1.8.1 DexFile(String fileName)

public DexFile(String fileName) throws IOException {
    //mCookie是一个Object类型的对象,用这个对象来保存dex文件也可能是dex文件列表
    //之后在加载class的时候,会把这个对象传入,从而加载相应的class
    mCookie = openDexFile(fileName, null, 0);
    mFileName = fileName;
    guard.open("close");
}

1.8.2 DexFile.loadDex()

static public DexFile loadDex(String sourcePathName, String outputPathName,
    int flags) throws IOException {

    return new DexFile(sourcePathName, outputPathName, flags);
}

1.8.3 DexFile()

private DexFile(String sourceName, String outputName, int flags) throws IOException {
    if (outputName != null) {
        try {
            String parent = new File(outputName).getParent();
            //确保输出目录属于当前应用,即输出目录是应用的私有目录/data/data/package_name/xxx
            if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
                throw new IllegalArgumentException("Optimized data directory " + parent
                            + " is not owned by the current user. Shared storage cannot protect"
                            + " your application from code injection attacks.");
            }
        } catch (ErrnoException ignored) {
        }
    }
    //mCookie是一个Object类型的对象,用这个对象来保存dex文件也可能是dex文件列表
    //之后在加载class的时候,会把这个对象传入,从而加载相应的class
    mCookie = openDexFile(sourceName, outputName, flags);
    mFileName = sourceName;
    guard.open("close");
}

从以上代码片段中可以看出,最后加载dex文件都是调用DexFile.openDexFile这个方法

1.9. DexFile.openDexFile()

private static Object openDexFile(String sourceName, String outputName, int flags) throws IOException {
    return openDexFileNative(new File(sourceName).getAbsolutePath(),
                              (outputName == null) ? null : new File(outputName).getAbsolutePath(),
                              flags);
}

openDexFileNative是一个native方法,之后的调用进入native层

二.Native调用链

2.1 DexFile_openDexFileNative()

static jobject DexFile_openDexFileNative(
    JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {

    ...
    ClassLinker* linker = Runtime::Current()->GetClassLinker();
    std::vector<std::unique_ptr<const DexFile>> dex_files;
    std::vector<std::string> error_msgs;

    dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs);

    if (!dex_files.empty()) {
        jlongArray array = ConvertNativeToJavaArray(env, dex_files);
        ... //错误处理,释放相应资源

        //返回给Java层,由DexFile的mCookie指向
        return array;
    } else {
        ... //抛出异常
        return nullptr;
    }
}

2.2 ClassLinker::OpenDexFilesFromOat()

std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
        const char* dex_location, const char* oat_location,
        std::vector<std::string>* error_msgs) {

    CHECK(error_msgs != nullptr);

    // Verify we aren't holding the mutator lock, which could starve GC if we
    // have to generate or relocate an oat file.
    Locks::mutator_lock_->AssertNotHeld(Thread::Current());

    //Runtime::Current()->IsAotCompiler用来判断Runtime是否用于dex2oat,dex2oat程序
    //也会创建一个Runtime,专门用来Compile
    OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
       !Runtime::Current()->IsAotCompiler());

    // Lock the target oat location to avoid races generating and loading the
    // oat file.
    std::string error_msg;
    if (!oat_file_assistant.Lock(&error_msg)) {
        ...
    }

    // Check if we already have an up-to-date oat file open.
    const OatFile* source_oat_file = nullptr;
    {
        ReaderMutexLock mu(Thread::Current(), dex_lock_);
        //oat_file_变量属于ClassLinker,用来存放oat文件
        for (const OatFile* oat_file : oat_files_) {
            ...
            // 判断oat文件是否正确
            if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
                source_oat_file = oat_file;
                break;
            }
        }
    }

    // If we didn't have an up-to-date oat file open, try to load one from disk.
    if (source_oat_file == nullptr) {
        // Update the oat file on disk if we can. This may fail, but that's okay.
        // Best effort is all that matters here.
        // 根据dex文件产生对应的oat文件
        if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
            ...
        }

        // Get the oat file on disk.
        std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
        if (oat_file.get() != nullptr) {
            // Take the file only if it has no collisions, or we must take it because of preopting.
            bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg);
            if (!accept_oat_file) {
                ...
                // However, if the app was part of /system and preopted, there is no original dex file
                // available. In that case grudgingly accept the oat file.
                if (!DexFile::MaybeDex(dex_location)) {
                    accept_oat_file = true;
                    ...
                }
            }

            if (accept_oat_file) {
                source_oat_file = oat_file.release();
                //将oat_file添加至oat_file_
                RegisterOatFile(source_oat_file);
            }
        }
    }

    std::vector<std::unique_ptr<const DexFile>> dex_files;

    // Load the dex files from the oat file.
    if (source_oat_file != nullptr) {
        //通过OatFileAssistant加载dex文件
        dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
        if (dex_files.empty()) {
            ...
        }
    }

    // Fall back to running out of the original dex file if we couldn't load any
    // dex_files from the oat file.
    if (dex_files.empty()) {
        ...
    }
    return dex_files;
}

2.3 OatFileAssistant::LoadDexFiles()

std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
    const OatFile& oat_file, const char* dex_location) {

    std::vector<std::unique_ptr<const DexFile>> dex_files;

    //先加载主dex文件.
    //先从OatFile中获取OatDexFile, 在oat文件中每一个OatDexFile记录了相应的
    //dex文件的文件名,文件偏移地址等关键信息
    std::string error_msg;
    const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
        dex_location, nullptr, false);
    if (oat_dex_file == nullptr) {
        ...
        return std::vector<std::unique_ptr<const DexFile>>();
    }
    //通过OatDexFile加载主dex, [2.4]
    std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
    if (dex_file.get() == nullptr) {
        LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
        return std::vector<std::unique_ptr<const DexFile>>();
    }
    dex_files.push_back(std::move(dex_file));

    //加载其余的次级dex文件
    for (size_t i = 1; ; i++) {
        //先获取次级dex文件的索引位置
        std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
        //根据索引位置获取对应的OatDexFile结构
        oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr, false);
        if (oat_dex_file == nullptr) {
            // There are no more secondary dex files to load.
            break;
        }
        //加载次级dex文件
        dex_file = oat_dex_file->OpenDexFile(&error_msg);
        if (dex_file.get() == nullptr) {
            LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
            return std::vector<std::unique_ptr<const DexFile>>();
        }
        dex_files.push_back(std::move(dex_file));
    }
    return dex_files;
}

每一个dex文件的信息都被封装在一个OatDexFile中
OatDexFile数据结构:
const OatFile* const oat_file_:指向oat文件的指针
const std::string dex_file_location_:dex文件名
const std::string_canonical_dex_file_location_:dex文件绝对路径
const uint32_t dex_file_location_cheksum_:dex文件名的校验和
const uint8_t* const dex_file_pointer_:指向对应dex文件在oat文件中相对于oatdata的偏移地址
const uint32_t* const oat_class_offsets_pointer_:指向属于该dex文件的OatClass相对于oatdata的偏移地址

2.4 OatDexFile::OpenDexFile()

std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
    return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
                       dex_file_location_checksum_, this, error_msg);
}

2.5 DexFile::Open()

static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
                                          const std::string& location,
                                          uint32_t location_checksum,
                                          const OatDexFile* oat_dex_file,
                                          std::string* error_msg) {
    return OpenMemory(base, size, location, location_checksum, nullptr, oat_dex_file, error_msg);
}

2.6 DexFile::OpenMemory()

std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base,
                                                   size_t size,
                                                   const std::string& location,
                                                   uint32_t location_checksum,
                                                   MemMap* mem_map,//nullptr
                                                   const OatDexFile* oat_dex_file,
                                                   std::string* error_msg) {
    //确保以4字节对齐
    CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned
    //初始化一个DexFile对象
    std::unique_ptr<DexFile> dex_file(
        new DexFile(base, size, location, location_checksum, mem_map, oat_dex_file));
    //检查dex的magic和version是否正确
    if (!dex_file->Init(error_msg)) {
        dex_file.reset();
    }
    return std::unique_ptr<const DexFile>(dex_file.release());
}

2.7 DexFile()

DexFile::DexFile(const uint8_t* base, size_t size,
                 const std::string& location,
                 uint32_t location_checksum,
                 MemMap* mem_map,
                 const OatDexFile* oat_dex_file)
      : begin_(base),
        size_(size),
        location_(location),
        location_checksum_(location_checksum),
        mem_map_(mem_map),//nullptr
        header_(reinterpret_cast<const Header*>(base)),
        string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
        type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
        field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
        method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
        proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
        class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
        find_class_def_misses_(0),
        class_def_index_(nullptr),
        oat_dex_file_(oat_dex_file) {
      CHECK(begin_ != nullptr) << GetLocation();
      CHECK_GT(size_, 0U) << GetLocation();
}

DexFile数据结构
const uint8_t* const begin_;dex文件的起始地址
const size_t size_: dex文件的大小,以字节为单位
const std::string location_:dex文件的文件名
const uint32_t location_checksum_:dex文件名的校验和
std::unique_ptr<MemMap> mem_map_:
const Header* const header_:指向dex文件的头部
const StringId* const string_ids_:指向dex文件StringId列表
const TypeId* const type_ids:指向dex文件TypeId列表
const FieldId* const field_ids_:指向dex文件FieldId列表
const MethodId* const method_ids_:指向dex文件MethodId列表
const ProtoId* const proto_ids_:指向dex文件ProtoID列表
const ClassDef* const class_defs_:指向dex文件ClassDef列表

三.总结

应用程序启动时,系统会创建属于该应用进程的PathClassLoader,创建PathClassLoader的过程就是加载dex文件的过程.
ART内部支持加载多个dex文件,这是因为ART采用AOT方式,在应用安装的时候会将apk文件的所有dex文件统一编译到一个oat文件中.
其实加载过程就是根据oat文件在Java层和Native层生成相应的数据结构,从而方便之后加载类和方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容