Android Dalvik 下的 dex 文件加载

0x01 前言

学习一下 Android 系统关于 dex 文件加载的过程,已经有很多前辈分享过无数的文章了,我只在这里做一个简单的记录,有助于自己查阅和学习 ART 虚拟机。这里分析使用的代码时基于 4.2.2 版本的系统。

0x02 一些必要的结构体

在 dex 文件加载过程中 java 中的类其实涉及并不多,主要还是集中在 native 层
下面就简单罗列一下(建议提前熟悉一下 DEX 文件结构):

// DexOrJar 结构体 主要是表示一个已经加载好的 dex 一些基本信息
struct DexOrJar {
    char*       fileName;	// 文件名
    bool        isDex;       	// 是 dex 还是 jar (也有可能是一个 jar 文件)
    bool        okayToFree; 
    RawDexFile* pRawDexFile;
    JarFile*    pJarFile;				// 如果是jar包,则指向JarFile结构 
    u1*         pDexMemory;  // 如果是dex文件,则指向dex文件存放的内存区 };
struct RawDexFile {
29    char*       cacheFileName;
30    DvmDex*     pDvmDex;
31};
32
struct DvmDex {
38    /* pointer to the DexFile we're associated with */
39    DexFile*            pDexFile;
40
41    /* clone of pDexFile->pHeader (it's used frequently enough) */
42    const DexHeader*    pHeader;
43
44    /* interned strings; parallel to "stringIds" */
45    struct StringObject** pResStrings;
46
47    /* resolved classes; parallel to "typeIds" */
48    struct ClassObject** pResClasses;
49
50    /* resolved methods; parallel to "methodIds" */
51    struct Method**     pResMethods;
52
53    /* resolved instance fields; parallel to "fieldIds" */
54    /* (this holds both InstField and StaticField) */
55    struct Field**      pResFields;
56
57    /* interface method lookup cache */
58    struct AtomicCache* pInterfaceCache;
59
60    /* shared memory region with file contents */
61    bool                isMappedReadOnly;
62    MemMapping          memMap;
63
64    /* lock ensuring mutual exclusion during updates */
65    pthread_mutex_t     modLock;
66};

0x003 正文

一般的类加载 都会用 DexClassloader,所以从入口开始看

1. DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

没干别的,就是将四个参数传给了父类构造函数

2. BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader {
	private final DexPathList pathList;
	public BaseDexClassLoader(
			String dexPath, 
			File optimizedDirectory,
            String libraryPath, 
            ClassLoader parent
            ) {
        super(parent);

        this.originalPath = dexPath;
        this.originalLibraryPath = libraryPath;
        this.pathList =
            new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
}

在构造函数里将传入参数保存一下,然后就是调用 DexPathList 的构造函数创建对象

3. DexPathList
/*package*/ final class DexPathList {

    /** class definition context */
    private final ClassLoader definingContext;

    /** list of dex/resource (class path) elements */
    private final Element[] dexElements;

    /** list of native library directory elements */
    private final File[] nativeLibraryDirectories;

    public DexPathList(
    		ClassLoader definingContext, 
    		String dexPath,
            String libraryPath, 
            File optimizedDirectory) {
            
        if (definingContext == null) {
            throw new NullPointerException("definingContext == null");
        }

        if (dexPath == null) {
            throw new NullPointerException("dexPath == null");
        }

        if (optimizedDirectory != null) {
            if (!optimizedDirectory.exists())  {
                throw new IllegalArgumentException(
                        "optimizedDirectory doesn't exist: "
                        + optimizedDirectory);
            }

            if (!(optimizedDirectory.canRead()
                            && optimizedDirectory.canWrite())) {
                throw new IllegalArgumentException(
                        "optimizedDirectory not readable/writable: "
                        + optimizedDirectory);
            }
        }

        this.definingContext = definingContext;
        this.dexElements =
            makeDexElements(splitDexPath(dexPath), optimizedDirectory);
        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    }

这个函数现实检查一下传入参数中 definingContext、dexPath、optimizedDirectory 都是不是空
然后调用 makeDexElements 函数,创建一个 Element 类的数组

		private static void splitAndAdd(String path, boolean wantDirectories,
158            ArrayList<File> resultList) {
159        if (path == null) {
160            return;
161        }
162
163        String[] strings = path.split(Pattern.quote(File.pathSeparator));
164
165        for (String s : strings) {
166            File file = new File(s);
167
168            if (! (file.exists() && file.canRead())) {
169                continue;
170            }
171
172            /*
173             * Note: There are other entities in filesystems than
174             * regular files and directories.
175             */
176            if (wantDirectories) {
177                if (!file.isDirectory()) {
178                    continue;
179                }
180            } else {
181                if (!file.isFile()) {
182                    continue;
183                }
184            }
185
186            resultList.add(file);
187        }
188    }
189

splitDexPath(dexPath)最终会掉用到上面这个函数,就是将传入路径下的文件都放到一个 ArrayList 中。

    private static Element[] makeDexElements(ArrayList<File> files,
195            File optimizedDirectory) {
196        ArrayList<Element> elements = new ArrayList<Element>();
197
198        /*
199         * Open all files and load the (direct or contained) dex files
200         * up front.
201         */
202        for (File file : files) {
203            File zip = null;
204            DexFile dex = null;
205            String name = file.getName();
206
207            if (name.endsWith(DEX_SUFFIX)) {
208                // Raw dex file (not inside a zip/jar).
209                try {
210                    dex = loadDexFile(file, optimizedDirectory);
211                } catch (IOException ex) {
212                    System.logE("Unable to load dex file: " + file, ex);
213                }
214            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
215                    || name.endsWith(ZIP_SUFFIX)) {
216                zip = file;
217
218                try {
219                    dex = loadDexFile(file, optimizedDirectory);
220                } catch (IOException ignored) {
221                    /*
222                     * IOException might get thrown "legitimately" by
223                     * the DexFile constructor if the zip file turns
224                     * out to be resource-only (that is, no
225                     * classes.dex file in it). Safe to just ignore
226                     * the exception here, and let dex == null.
227                     */
228                }
229            } else {
230                System.logW("Unknown file type for: " + file);
231            }
232
233            if ((zip != null) || (dex != null)) {
234                elements.add(new Element(file, zip, dex));
235            }
236        }
237
238        return elements.toArray(new Element[elements.size()]);
239    }

makeDexElements 根据文件的类型调用 loadDexFile 函数,生成一个 DexFile 对象,并将它发到数组中保存。

		private static DexFile loadDexFile(File file, File optimizedDirectory)
246            throws IOException {
247        if (optimizedDirectory == null) {
248            return new DexFile(file);
249        } else {
250            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
251            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
252        }
253    }

loadDexFile 调用 DexFile 构造函数,或者 DexFile.loadDex 生成一个 DexFile 对象。

4. DexFile

其实 DexFile.loadDex 最终也是调用 DexFile 构造函数

	static public DexFile loadDex(String sourcePathName, String outputPathName,
140        int flags) throws IOException {
149        return new DexFile(sourcePathName, outputPathName, flags);
150    }
	public DexFile(String fileName) throws IOException {
78        mCookie = openDexFile(fileName, null, 0);
79        mFileName = fileName;
80        guard.open("close");
81        //System.out.println("DEX FILE cookie is " + mCookie);
82    }

	native private static int openDexFile(String sourceName, String outputName,
273        int flags) throws IOException;

在 DexFile 构造函数中调用了 openDexFile ,并将结果保存在 int 类型的 mCookie 字段中, openDexFile 是一个 native 函数。

5. OpenDexFileNative

代码路径在:/dalvik/vm/native/dalvik_system_DexFile.cpp


151static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
152    JValue* pResult)
153{
154    StringObject* sourceNameObj = (StringObject*) args[0];
155    StringObject* outputNameObj = (StringObject*) args[1];
156    DexOrJar* pDexOrJar = NULL;
157    JarFile* pJarFile;
158    RawDexFile* pRawDexFile;
159    char* sourceName;
160    char* outputName;
202		........
203    /*
204     * Try to open it directly as a DEX if the name ends with ".dex".
205     * If that fails (or isn't tried in the first place), try it as a
206     * Zip with a "classes.dex" inside.
207     */
208    if (hasDexExtension(sourceName)
209            && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
210        ALOGV("Opening DEX file '%s' (DEX)", sourceName);
211
212        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
213        pDexOrJar->isDex = true;
214        pDexOrJar->pRawDexFile = pRawDexFile;
215        pDexOrJar->pDexMemory = NULL;
216    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
217        ALOGV("Opening DEX file '%s' (Jar)", sourceName);
218
219        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
220        pDexOrJar->isDex = false;
221        pDexOrJar->pJarFile = pJarFile;
222        pDexOrJar->pDexMemory = NULL;
223    } else {
224        ALOGV("Unable to open DEX file '%s'", sourceName);
225        dvmThrowIOException("unable to open DEX file");
226    }
227
228    if (pDexOrJar != NULL) {
229        pDexOrJar->fileName = sourceName;
230        addToDexFileTable(pDexOrJar);
231    } else {
232        free(sourceName);
233    }
234
235    RETURN_PTR(pDexOrJar);
236}

首先是一些检查,紧接着调用 dvmRawDexFileOpen 尝试打开 dex 文件,如果失败就会使用 dvmJarFileOpen 尝试打开一个 jar 文件,将结果保存在一个 pDexOrJar 结构中,最后将这个结构体的指针返回给 java 层保存。
这里只分析 dex 的情况,其实 jar 也是差不多的。

dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false)

这个函数有点长,一段一段分析吧

int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
110    RawDexFile** ppRawDexFile, bool isBootstrap)
111{
112    /*
113     * TODO: This duplicates a lot of code from dvmJarFileOpen() in
114     * JarFile.c. This should be refactored.
115     */
116
117    DvmDex* pDvmDex = NULL;
118    char* cachedName = NULL;
119    int result = -1;
120    int dexFd = -1;
121    int optFd = -1;
122    u4 modTime = 0;
123    u4 adler32 = 0;
124    size_t fileSize = 0;
125    bool newFile = false;
126    bool locked = false;
127
128    dexFd = open(fileName, O_RDONLY);
129    if (dexFd < 0) goto bail;
130
131    /* If we fork/exec into dexopt, don't let it inherit the open fd. */
132    dvmSetCloseOnExec(dexFd);
133
134    if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
135        ALOGE("Error with header for %s", fileName);
136        goto bail;
137    }
138
139    if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
140        ALOGE("Error with stat for %s", fileName);
141        goto bail;
142    }
143

首先使用 open 函数打开 dex 文件, 然后做一些魔数、adler32值、时间和文件大小的检查,并且 adler32 和文件大小保存下来,后面要用。

if (odexOutputName == NULL) {
151        cachedName = dexOptGenerateCacheFileName(fileName, NULL);
152        if (cachedName == NULL)
153            goto bail;
154    } else {
155        cachedName = strdup(odexOutputName);
156    }
157
158    ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
159            fileName, cachedName);
160
161    optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
162        adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
163
164    if (optFd < 0) {
165        ALOGI("Unable to open or create cache for %s (%s)",
166                fileName, cachedName);
167        goto bail;
168    }
169    locked = true;

这段代码主要功能就是,为 dex 优化准备一个路径,并创建一个空文件
dexOptGenerateCacheFileName 是用于字符串拼接,拼接一个缓存文件的路径, 然后看看 dvmOpenCachedDexFile

int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
130    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
131{
132    int fd, cc;
133    struct stat fdStat, fileStat;
134    bool readOnly = false;
135
136    *pNewFile = false;
137
138retry:
139    /*
140     * Try to open the cache file.  If we've been asked to,
141     * create it if it doesn't exist.
142     */
143    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
	   // 一些容错检查

168    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
169    cc = flock(fd, LOCK_EX | LOCK_NB);
	   // 一些获取文件锁 容错检查
	   
189    cc = fstat(fd, &fdStat);
195    cc = stat(cacheFileName, &fileStat);

214    if (fdStat.st_size == 0) {
215        if (readOnly) {
216            ALOGW("DexOpt: file has zero length and isn't writable");
217            goto close_fail;
218        }
219        cc = dexOptCreateEmptyHeader(fd);
220        if (cc != 0)
221            goto close_fail;
222        *pNewFile = true;
223        ALOGV("DexOpt: successfully initialized new cache file");
224    } else {
225        ......
247        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
248                expectVerify, expectOpt))
249        {
250            if (readOnly) {
257                if (createIfMissing) {
258                    ALOGW("Cached DEX '%s' (%s) is stale and not writable",
259                        fileName, cacheFileName);
260                }
261                goto close_fail;
262            }
285            ALOGD("ODEX file is stale or bad; removing and retrying (%s)",
286                cacheFileName);
287            if (ftruncate(fd, 0) != 0) {
288                ALOGW("Warning: unable to truncate cache file '%s': %s",
289                    cacheFileName, strerror(errno));
290                /* keep going */
291            }
292            if (unlink(cacheFileName) != 0) {
293                ALOGW("Warning: unable to remove cache file '%s': %d %s",
294                    cacheFileName, errno, strerror(errno));
295                /* keep going; permission failure should probably be fatal */
296            }
297            LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
298            flock(fd, LOCK_UN);
299            close(fd);
300            goto retry;
301        } else {
302            ALOGV("DexOpt: good deps in cache file");
303        }
304    }
305
306    assert(fd >= 0);
307    return fd;
308
309close_fail:
310    flock(fd, LOCK_UN);
311    close(fd);
312    return -1;
313}

这个函数比较长我删除一些错误处理代码,首先调用 open 打开文件,打开的时候如果没有就会创建一个,并用 fclock 函数获取文件锁,防止文件被其他进程操作, 之后再使用 fstat 获取文件状态,比较一下 cacheFileName 和使用 open 函数打开的那个文件是不是同一个文件。 检查 fdStat.st_size == 0,说明这个文件是当前进程创建的,之后会调用 dexOptCreateEmptyHeade 在缓存文件中构造一个空的 OptHeadser 结构,将 pNewFile 变量设置为 true 说明 这是一个新文件,将文件描述符返回。

现在继续往下执行,从 dvmOpenCachedDexFile 返回后, 有了一个缓存(Opt)文件,并且OptHeader 已经创建并写入到了文件开始的部分。

 if (newFile) {
178        u8 startWhen, copyWhen, endWhen;
179        bool result;
180        off_t dexOffset;
181
182        dexOffset = lseek(optFd, 0, SEEK_CUR);
183        result = (dexOffset > 0);
184
185        if (result) {
186            startWhen = dvmGetRelativeTimeUsec();
187            result = copyFileToFile(optFd, dexFd, fileSize) == 0;
188            copyWhen = dvmGetRelativeTimeUsec();
189        }
190
191        if (result) {
192            result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
193                fileName, modTime, adler32, isBootstrap);
194        }
195
196        if (!result) {
197            ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
198            goto bail;
199        }
200
201        endWhen = dvmGetRelativeTimeUsec();
202        ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
203            fileName,
204            (int) (copyWhen - startWhen) / 1000,
205            (int) (endWhen - copyWhen) / 1000);
206    }

因为刚才在将建缓存文件时将 newFile 设置为了 true,这里执行括号里面的代码。开始是一些文件操作, 将 dex 文件的内容复制到缓存文件中,位置是紧挨着 OptHeader。 然后调用 dvmOptimizeDexFile, 这个函数就是调用 /bin/dexopt 对 dex 文件进行优化。(这里就不分析了) 继续向下执行

if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
213        ALOGI("Unable to map cached %s", fileName);
214        goto bail;
215    }
216
217    if (locked) {
218        /* unlock the fd */
219        if (!dvmUnlockCachedDexFile(optFd)) {
220            /* uh oh -- this process needs to exit or we'll wedge the system */
221            ALOGE("Unable to unlock DEX file");
222            goto bail;
223        }
224        locked = false;
225    }
226
227    ALOGV("Successfully opened '%s'", fileName);
228
229    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
230    (*ppRawDexFile)->cacheFileName = cachedName;
231    (*ppRawDexFile)->pDvmDex = pDvmDex;
232    cachedName = NULL;      // don't free it below
233    result = 0;

dvmRawDexFileOpen 函数调用的最后, 就是构造一个 DvmDex 结构,然后保存在参数 ppRawDexFile 的 RawDexFile 结构中返回给上层。

看一下 dvmDexFileOpenFromFd 是如何构造一个 DvmDex 结构的。

int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
92{
93    DvmDex* pDvmDex;
94    DexFile* pDexFile;
95    MemMapping memMap;
96    int parseFlags = kDexParseDefault;
97    int result = -1;
98
99    if (gDvm.verifyDexChecksum)
100        parseFlags |= kDexParseVerifyChecksum;
101
102    if (lseek(fd, 0, SEEK_SET) < 0) {
103        ALOGE("lseek rewind failed");
104        goto bail;
105    }
106
107    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
108        ALOGE("Unable to map file");
109        goto bail;
110    }
111
112    pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
113    if (pDexFile == NULL) {
114        ALOGE("DEX parse failed");
115        sysReleaseShmem(&memMap);
116        goto bail;
117    }
118
119    pDvmDex = allocateAuxStructures(pDexFile);
120    if (pDvmDex == NULL) {
121        dexFileFree(pDexFile);
122        sysReleaseShmem(&memMap);
123        goto bail;
124    }
125
126    /* tuck this into the DexFile so it gets released later */
127    sysCopyMap(&pDvmDex->memMap, &memMap);
128    pDvmDex->isMappedReadOnly = true;
129    *ppDvmDex = pDvmDex;
130    result = 0;
131
132bail:
133    return result;
134}

首先将文件映射到内存中,然后使用 dexFileParse 解析 dex 文件,最后填充 DvmDex 字段返回给调用者。

最后再看看 dexFileParse 是怎么实现的

289DexFile* dexFileParse(const u1* data, size_t length, int flags)
290{
291    DexFile* pDexFile = NULL;
292    const DexHeader* pHeader;
293    const u1* magic;
294    int result = -1;
295
296    if (length < sizeof(DexHeader)) {
297        ALOGE("too short to be a valid .dex");
298        goto bail;      /* bad file format */
299    }
300
301    pDexFile = (DexFile*) malloc(sizeof(DexFile));
302    if (pDexFile == NULL)
303        goto bail;      /* alloc failure */
304    memset(pDexFile, 0, sizeof(DexFile));
305
306    /*
307     * Peel off the optimized header.
308     */
309    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
310        magic = data;
311        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
312            ALOGE("bad opt version (0x%02x %02x %02x %02x)",
313                 magic[4], magic[5], magic[6], magic[7]);
314            goto bail;
315        }
316
317        pDexFile->pOptHeader = (const DexOptHeader*) data;
318        ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",
319            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
320
321        /* parse the optimized dex file tables */
322        if (!dexParseOptData(data, length, pDexFile))
323            goto bail;
324
325        /* ignore the opt header and appended data from here on out */
326        data += pDexFile->pOptHeader->dexOffset;
327        length -= pDexFile->pOptHeader->dexOffset;
328        if (pDexFile->pOptHeader->dexLength > length) {
329            ALOGE("File truncated? stored len=%d, rem len=%d",
330                pDexFile->pOptHeader->dexLength, (int) length);
331            goto bail;
332        }
333        length = pDexFile->pOptHeader->dexLength;
334    }
335
336    dexFileSetupBasicPointers(pDexFile, data);
337    pHeader = pDexFile->pHeader;
338
339    if (!dexHasValidMagic(pHeader)) {
340        goto bail;
341    }
342
343    /*
344     * Verify the checksum(s).  This is reasonably quick, but does require
345     * touching every byte in the DEX file.  The base checksum changes after
346     * byte-swapping and DEX optimization.
347     */
348    if (flags & kDexParseVerifyChecksum) {
349        u4 adler = dexComputeChecksum(pHeader);
350        if (adler != pHeader->checksum) {
351            ALOGE("ERROR: bad checksum (%08x vs %08x)",
352                adler, pHeader->checksum);
353            if (!(flags & kDexParseContinueOnError))
354                goto bail;
355        } else {
356            ALOGV("+++ adler32 checksum (%08x) verified", adler);
357        }
358
359        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
360        if (pOptHeader != NULL) {
361            adler = dexComputeOptChecksum(pOptHeader);
362            if (adler != pOptHeader->checksum) {
363                ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
364                    adler, pOptHeader->checksum);
365                if (!(flags & kDexParseContinueOnError))
366                    goto bail;
367            } else {
368                ALOGV("+++ adler32 opt checksum (%08x) verified", adler);
369            }
370        }
371    }
372
373    /*
374     * Verify the SHA-1 digest.  (Normally we don't want to do this --
375     * the digest is used to uniquely identify the original DEX file, and
376     * can't be computed for verification after the DEX is byte-swapped
377     * and optimized.)
378     */
379    if (kVerifySignature) {
380        unsigned char sha1Digest[kSHA1DigestLen];
381        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
382                            kSHA1DigestLen;
383
384        dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
385        if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
386            char tmpBuf1[kSHA1DigestOutputLen];
387            char tmpBuf2[kSHA1DigestOutputLen];
388            ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
389                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
390                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
391            if (!(flags & kDexParseContinueOnError))
392                goto bail;
393        } else {
394            ALOGV("+++ sha1 digest verified");
395        }
396    }
397
398    if (pHeader->fileSize != length) {
399        ALOGE("ERROR: stored file size (%d) != expected (%d)",
400            (int) pHeader->fileSize, (int) length);
401        if (!(flags & kDexParseContinueOnError))
402            goto bail;
403    }
404
405    if (pHeader->classDefsSize == 0) {
406        ALOGE("ERROR: DEX file has no classes in it, failing");
407        goto bail;
408    }
409
410    /*
411     * Success!
412     */
413    result = 0;
414
415bail:
416    if (result != 0 && pDexFile != NULL) {
417        dexFileFree(pDexFile);
418        pDexFile = NULL;
419    }
420    return pDexFile;
421}

函数很长,但不复杂。就是根据 DEX 文件结构解析文件,并填充相应字段。

行了,基本上主要的流程都分析了,这里简单的回顾一下流程。

DexCLassLoader -> BaseDexClassLoader -> DexPathList -> DexFile -> OpenDexFileNative -> dvmRawDexFileOpen (这里就是优化、解析 Dex 文件)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值