我们可以利用DexClassLoader来实现动态加载dex文件,而很多资料也只是对于DexClassLoader的使用进行了介绍,没有深入讲解dex的动态加载机制,我们就借助于Android4.4的源码来探索。先从一个简单的动态加载dex文件开始 具体实现细节可以参考这篇文章AndroidDex数据动态加载技术

Android4.4的源码在百度网盘分享: Android 4.4源码下载

先是我们要封装到text.jar文件中的很简单的调用函数,只是简单的产生Toast:

/*
* 对外接口
*/
public interface Iinterface {
public void call();
public String getData(); }
public class IClass implements Iinterface{
private Context context;
public IClass(Context context){
super();
this.context = context;
}
//@Override
public void call() {
// TODO Auto-generated method stub
Toast.makeText(context, "call method", 0).show();
}
//@Override
public String getData() {
// TODO Auto-generated method stub
return "Hello ,I am from IClass";
}
}

在MainActivity中只是解压test.jar文件,然后通过DexClassLoader类来加载dex文件,最后通过反射调用相关方法:

public class FileUtile {
//MainActivity "testdex.jar", "testdex.jar"
public static void CopyAssertJarToFile(Context context, String filename,
String des) {
try {
//返回 File ,获取外部存储目录即 SDCard
//path "/mnt/sdcard/testdex.jar"
//File.separator Windows \ linux /
File file = new File(Environment.getExternalStorageDirectory().getPath()
+ File.separator + des);
if (file.exists()) {
return;
}
//取得资源文件的输入流
InputStream inputStream = context.getAssets().open(filename);
file.createNewFile(); //创建"/mnt/sdcard/testdex.jar" 文件
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte buffer[] = new byte[1024];
int len = 0;
while ((len = inputStream.read(buffer)) != 0) {
fileOutputStream.write(buffer, 0, len);
}
inputStream.close();
fileOutputStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FileUtile.CopyAssertJarToFile(this, "testdex.jar", "testdex.jar");
/*拷贝*/
File file = new File(Environment.getExternalStorageDirectory()
.toString() + File.separator + "testdex.jar");
final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
/*
* Parameters
dexPath 需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 ":"
optimizedDirectory 优化后的dex文件存放目录,不能为null
libraryPath 目标类中使用的C/C++库的列表,每个目录用File.pathSeparator间隔开; 可以为 null
parent 该类装载器的父装载器,一般用当前执行类的装载器
*/
DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(), null,
getClassLoader());
try {
Class<?> iclass = classLoader.loadClass("com.demo.dex.IClass");
Constructor<?> istructor = iclass.getConstructor(Context.class);
//利用反射原理去调用
Method method = iclass.getMethod("call", null);
String data = (String) method.invoke(istructor.newInstance(this), null);
//System.out.println(data);
Log.d("CCDebug",data);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

我们从DexClassLoaderl类开始分析:

在\libcore\dalvik\src\main\java\dalvik\system\ DexClassLoader.java文件下

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

非常简单的DexClassLoader的构造函数,只是调用了父类BaseDexClassLoader的构造函数,在同一目录下的BaseDexClassLoader.java的源码:

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}

同样的,也是很简单的调用父类ClassLoader的构造函数,然后生成一个DexPathList对象,在同一目录下的DexPathList.java文件中:

public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
//省略参数校验以及异常处理的代码
this.definingContext = definingContext;
……
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
suppressedExceptions);
……
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}

我们继续阅读DexPathList.java文件中makeDexElements 的关键代码:

private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions) {
// ……
for (File file : files) {
File zip = null;
DexFile dex = null;
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) { //.dex文件
// Raw dex file (not inside a zip/jar).
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ex) {
System.logE("Unable to load dex file: " + file, ex);
}
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
//.apk .jar .zip文件
zip = file;
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException suppressed) {
suppressedExceptions.add(suppressed);
}
} else 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 {
System.logW("Unknown file type for: " + file);
}
}
//……
return elements.toArray(new Element[elements.size()]);
}

DexPathList.java文件中:

private static DexFile loadDexFile(File file, File optimizedDirectory)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
} //生成odex的目录
private static String optimizedPathFor(File path,
File optimizedDirectory) {
String fileName = path.getName();
if (!fileName.endsWith(DEX_SUFFIX)) {
int lastDot = fileName.lastIndexOf(".");
if (lastDot < 0) {
fileName += DEX_SUFFIX;
} else {
StringBuilder sb = new StringBuilder(lastDot + 4);
sb.append(fileName, 0, lastDot);
sb.append(DEX_SUFFIX);
fileName = sb.toString();
}
}
File result = new File(optimizedDirectory, fileName);
return result.getPath();
}

optimizedPathFor主要是对文件的后缀进行修正,如果没有后缀名,就在末尾加上.dex,如果文件结尾不是.dex,就将后缀替换为.dex,然后创建我们的.dex文件,然后返回我们创建的.dex文件的路径,继续执行DexFile.loadDex() 函数:

static public DexFile loadDex(String sourcePathName, String outputPathName,
int flags) throws IOException {
return new DexFile(sourcePathName, outputPathName, flags);
}

这里直接返回了一个DexFile对象,下面来看看这个类的构造函数:

//sourceName  就是我们要加载的自己的.jar文件路径
// outputName 在optimizedPathFor() 函数中修正的加载.dex的路径
private DexFile(String sourceName, String outputName, int flags) throws IOException {
if (outputName != null) {
try {
String parent = new File(outputName).getParent();
/* ??????*/
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) {
// assume we'll fail with a more contextual error later
}
}
//我们的重点就是在openDexFile()函数上
mCookie = openDexFile(sourceName, outputName, flags);
mFileName = sourceName;
guard.open("close");
//System.out.println("DEX FILE cookie is " + mCookie);
}

openDexFile函数的返回值是一个整型,保存在mCookie中,文件名保存在mFileName中

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

在openDexFile()中只是调用了openDexFileNative () 继续跟入在\ dalvik\v m\nat ive\dalvik _sys tem_DexFile.cpp文件中的openDexFileNative() 函数,接下重点就在这个函数:

static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
JValue* pResult)
{
//args[0]: sourceName java层传入的
//args[1]: outputName
StringObject* sourceNameObj = (StringObject*) args[0];
StringObject* outputNameObj = (StringObject*) args[1];
DexOrJar* pDexOrJar = NULL;
JarFile* pJarFile;
RawDexFile* pRawDexFile;
//DexOrJar* JarFile* RawDexFile* 目录
char* sourceName;
char* outputName;
//……
sourceName = dvmCreateCstrFromString(sourceNameObj);
if (outputNameObj != NULL)
outputName = dvmCreateCstrFromString(outputNameObj);
else
outputName = NULL;
/*判断要加载的dex是否为系统中的dex文件
* gDvm ???
*/
if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
dvmThrowIOException(
"Re-opening BOOTCLASSPATH DEX files is not allowed");
free(sourceName);
free(outputName);
RETURN_VOID();
} /*
* Try to open it directly as a DEX if the name ends with ".dex".
* If that fails (or isn't tried in the first place), try it as a
* Zip with a "classes.dex" inside.
*/
//判断sourcename扩展名是否是.dex
if (hasDexExtension(sourceName)
&& dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
ALOGV("Opening DEX file '%s' (DEX)", sourceName);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
pDexOrJar->pDexMemory = NULL;
//.jar文件
} else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
ALOGV("Opening DEX file '%s' (Jar)", sourceName);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = false;
pDexOrJar->pJarFile = pJarFile;
pDexOrJar->pDexMemory = NULL;
} else {
//都不满足,抛出异常
ALOGV("Unable to open DEX file '%s'", sourceName);
dvmThrowIOException("unable to open DEX file");
}
if (pDexOrJar != NULL) {
pDexOrJar->fileName = sourceName;
//把pDexOr这个结构体中的内容加到gDvm中的userDexFile结构的hash表中,便于Dalvik以后的查找
addToDexFileTable(pDexOrJar);
} else {
free(sourceName);
}
free(outputName);
RETURN_PTR(pDexOrJar);
}

接下来再看对.dex文件的处理函数dvmRawDexFileOpen 在dalvik\vm\RawDexFile.cpp文件中:

/* See documentation comment in header. */
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
RawDexFile** ppRawDexFile, bool isBootstrap)
{
DvmDex* pDvmDex = NULL;
char* cachedName = NULL;
int result = -1;
int dexFd = -1;
int optFd = -1;
u4 modTime = 0;
u4 adler32 = 0;
size_t fileSize = 0;
bool newFile = false;
bool locked = false;
dexFd = open(fileName, O_RDONLY); //打开dex文件
if (dexFd < 0) goto bail;
/* If we fork/exec into dexopt, don't let it inherit the open fd. */
dvmSetCloseOnExec(dexFd);//dexfd不继承
//校验dex文件的标志,将第8字节开始的4个字节赋值给adler32。
if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
ALOGE("Error with header for %s", fileName);
goto bail;
}
//得到dex文件的大小和修改时间,保存在modTime和filesize中
if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
ALOGE("Error with stat for %s", fileName);
goto bail;
} //odexOutputName就是odex文件名,如果odexOutputName为空,则自动生成一个。
if (odexOutputName == NULL) {
cachedName = dexOptGenerateCacheFileName(fileName, NULL);
if (cachedName == NULL)
goto bail;
} else {
cachedName = strdup(odexOutputName);
}
//主要是验证缓存文件名的正确性,之后将dexOptHeader结构写入fd中
optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
locked = true; if (newFile) {
u8 startWhen, copyWhen, endWhen;
bool result;
off_t dexOffset;
dexOffset = lseek(optFd, 0, SEEK_CUR); //文件指针的位置
result = (dexOffset > 0);
if (result) {
startWhen = dvmGetRelativeTimeUsec();
//将dex文件中的内容拷贝到当前odex文件,也就是dexOffset开始
result = copyFileToFile(optFd, dexFd, fileSize) == 0;
copyWhen = dvmGetRelativeTimeUsec();
}
if (result) {
//优化odex文件
result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
fileName, modTime, adler32, isBootstrap);
}
}
/*
* Map the cached version. This immediately rewinds the fd, so it
* doesn't have to be seeked anywhere in particular.
*/
//将odex文件映射到内存空间(mmap),并用mprotect将属性置为只读属性,并将映射的dex结构放在pDvmDex数据结构中,具体代码在下面。
if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
ALOGI("Unable to map cached %s", fileName);
goto bail;
}
……
}
//Dalvik/vm/RewDexFile.cpp
static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
{
u1 headerStart[];
ssize_t amt = read(fd, headerStart, sizeof(headerStart));
if (amt < ) {
ALOGE("Unable to read header: %s", strerror(errno));
return -;
}
if (amt != sizeof(headerStart)) {
ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
return -;
}
if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
return -;
}
*adler32 = (u4) headerStart[]
| (((u4) headerStart[]) << )
| (((u4) headerStart[]) << )
| (((u4) headerStart[]) << ); return ;
}
//dalvik\vm\DvmDex.cpp
/*
* Given an open optimized DEX file, map it into read-only shared memory and
* parse the contents.
*
* Returns nonzero on error.
*/
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
DvmDex* pDvmDex;
DexFile* pDexFile;
MemMapping memMap;
int parseFlags = kDexParseDefault;
int result = -; if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;
if (lseek(fd, , SEEK_SET) < ) {
ALOGE("lseek rewind failed");
goto bail;
}
//mmap映射fd文件,就是我们之前的odex文件
if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != ) {
ALOGE("Unable to map file");
goto bail;
}
pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
if (pDexFile == NULL) {
ALOGE("DEX parse failed");
sysReleaseShmem(&memMap);
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
sysReleaseShmem(&memMap);
goto bail;
}
/* tuck this into the DexFile so it gets released later */
//将映射odex文件的内存拷贝到DvmDex的结构中
sysCopyMap(&pDvmDex->memMap, &memMap);
pDvmDex->isMappedReadOnly = true;
*ppDvmDex = pDvmDex;
result = ; bail:
return result;
} /*dalvik\libdex\SysUtil.cpp
*/
int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
{
off_t start;
size_t length;
void* memPtr;
assert(pMap != NULL);
//获得文件长度和文件开始地址
if (getFileStartAndLength(fd, &start, &length) < )
return -;
//映射文件
memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
fd, start);
//……
//将保护属性置为只读属性
if (mprotect(memPtr, length, PROT_READ) < ) {
//…….
}
pMap->baseAddr = pMap->addr = memPtr;
pMap->baseLength = pMap->length = length;
return ;
//……
}

下面在分析文件后缀不是.dex的情况:

/*如果不是.dex文件*/
int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
JarFile** ppJarFile, bool isBootstrap)
{
ZipArchive archive;
DvmDex* pDvmDex = NULL;
char* cachedName = NULL;
bool archiveOpen = false;
bool locked = false;
int fd = -;
int result = -; //打开.jar文件并映射,内存结构放在ZipArchive中,之后将具体分析的代码
if (dexZipOpenArchive(fileName, &archive) != )
goto bail;
archiveOpen = true;
dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); //不继承 // openAlternateSuffix函数将fileName的后缀名改为”.odex”,例如
//”Hello.jar”--”Hello.odex”,然后调用open()”打开”Hello.odex文件
//如果成功返回”Hello.odex”的文件描述符
fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
if (fd >= ) {
ALOGV("Using alternate file (odex) for %s ...", fileName);
//…检验optHeader
if (!dvmCheckOptHeaderAndDependencies(fd, false, , , true, true)) {
//……
goto tryArchive;
}
} else {
ZipEntry entry;
tryArchive:
/*
* Pre-created .odex absent or stale. Look inside the jar for a
* "classes.dex".
*/
// static const char* kDexInJarName = "classes.dex";
/*
在dexZipFindEntry函数中,对kDexInJarName也就是”class.dex”进行hash运算,找到”class.dex”在archive结构中的表项
*/
entry = dexZipFindEntry(&archive, kDexInJarName);
if (entry != NULL) {
bool newFile = false; //如果odex缓存路径为空,则自动生成一个路径
if (odexOutputName == NULL) {
cachedName = dexOptGenerateCacheFileName(fileName,
kDexInJarName);
if (cachedName == NULL)
goto bail;
} else {
cachedName = strdup(odexOutputName);
}
//创建cachedName对应的文件 (.odex)
fd = dvmOpenCachedDexFile(fileName, cachedName,
dexGetZipEntryModTime(&archive, entry),
dexGetZipEntryCrc32(&archive, entry),
//……
locked = true;
//……
if (newFile) { //成功创建.odex文件
u8 startWhen, extractWhen, endWhen;
bool result;
off_t dexOffset;
dexOffset = lseek(fd, , SEEK_CUR);
result = (dexOffset > );
if (result) {
startWhen = dvmGetRelativeTimeUsec();
result = dexZipExtractEntryToFile(&archive, entry, fd) == ;
extractWhen = dvmGetRelativeTimeUsec();
}
if (result) {
//优化dex文件-.odex
result = dvmOptimizeDexFile(fd, dexOffset,
dexGetZipEntryUncompLen(&archive, entry),
fileName,
dexGetZipEntryModTime(&archive, entry),
dexGetZipEntryCrc32(&archive, entry),
isBootstrap);
}
//已经得到了.odex文件,下面的流程就和.dex文件一样了。
//映射.odex文件,
if (dvmDexFileOpenFromFd(fd, &pDvmDex) != )
//…………
return result;
}
//\dalvik\libdex\SysUtil.cpp
int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
{
int fd, err;
…….
memset(pArchive, , sizeof(ZipArchive));
//打开文件
fd = open(fileName, O_RDONLY | O_BINARY, );
……
return dexZipPrepArchive(fd, fileName, pArchive);
} int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
{
int result = -;
memset(pArchive, , sizeof(*pArchive));
pArchive->mFd = fd; //Zip的文件描述符
if (mapCentralDirectory(fd, debugFileName, pArchive) != )
goto bail;
if (parseZipArchive(pArchive) != ) {
goto bail;
}
/* success */
result = ;
bail:
if (result != )
dexZipCloseArchive(pArchive); //失败释放pArchive结构
return result;
} static int mapCentralDirectory(int fd, const char* debugFileName,
ZipArchive* pArchive)
{
/*
* Get and test file length.
*/
//检验文件长度的有效性
off64_t fileLength = lseek64(fd, , SEEK_END);
if (fileLength < kEOCDLen) {
return -;
}
size_t readAmount = kMaxEOCDSearch;
if (fileLength < off_t(readAmount))
readAmount = fileLength; u1* scanBuf = (u1*) malloc(readAmount);
if (scanBuf == NULL) {
return -;
}
int result = mapCentralDirectory0(fd, debugFileName, pArchive,
fileLength, readAmount, scanBuf);
free(scanBuf);
return result;
} tatic int mapCentralDirectory0(int fd, const char* debugFileName,
ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf)
{
/*
* Make sure this is a Zip archive.
*/
//校验文件是否合法的Zip文件
//…… //偏移16的地方 //偏移12
if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize,
&pArchive->mDirectoryMap) != )
{
ALOGW("Zip: cd map failed");
return -;
}
pArchive->mNumEntries = numEntries;
pArchive->mDirectoryOffset = centralDirOffset;
return ;
} int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
MemMapping* pMap)
{
size_t actualLength;
off_t actualStart;
int adjust;
void* memPtr;
assert(pMap != NULL);
/* adjust to be page-aligned */
adjust = start % SYSTEM_PAGE_SIZE;
actualStart = start - adjust;
actualLength = length + adjust;
//映射
memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
fd, actualStart);
// …….
pMap->baseAddr = memPtr;
pMap->baseLength = actualLength;
pMap->addr = (char*)memPtr + adjust;
pMap->length = length;
return ;
}

ZipArchive的结构体如下:

struct ZipArchive {
/* open Zip archive */
int mFd; //打开的zip文件
/* mapped central directory area */
off_t mDirectoryOffset;
MemMapping mDirectoryMap; //映射内存的结构
/* number of entries in the Zip archive */
int mNumEntries; //
int mHashTableSize; //名字hash表的大小
ZipHashEntry* mHashTable; //hash表的表项,
}; struct ZipHashEntry {
const char* name;
unsigned short nameLen;
};

我们可以简要总结下整个的加载流程,首先是对文件名的修正,后缀名置为”.dex”作为输出文件,然后生个一个DexPathList对象函数直接返回一个DexPathList对象,

在DexPathList的构造函数中调用makeDexElements()函数,在makeDexElement()函数中调用loadDexFile()开始对.dex或者是.jar .zip .apk文件进行处理,

跟入loadDexFile()函数中,会发现里面做的工作很简单,调用optimizedPathFor()函数对optimizedDiretcory路径进行修正。

之后才真正通过DexFile.loadDex()开始加载文件中的数据,其中的加载也只是返回一个DexFile对象。

在DexFile类的构造函数中,重点便放在了其调用的openDexFile()函数,在openDexFile()中调用了openDexFileNative()真正进入native层,

在openDexFileNative()的真正实现中,对于后缀名为.dex的文件或者其他文件(.jar .apk .zip)分开进行处理:

.dex文件调用dvmRawDexFileOpen();
其他文件调用dvmJarFileOpen()。

在dvmRawDexFileOpen()函数中,检验dex文件的标志,检验odex文件的缓存名称,之后将dex文件拷贝到odex文件中,并对odex进行优化

调用dvmDexFileOpenFromFd()对优化后的odex文件进行映射,通过mprotect置为"只读"属性并将映射的内存结构保存在DvmDex*结构中。

dvmJarFileOpen()先对文件进行映射,结构保存在ZipArchive中,然后再尝试以文件名作为dex文件名来“打开”文件,
如果失败,则调用dexZipFindEntry在ZipArchive的名称hash表中找名为"class.dex"的文件,然后创建odex文件,下面就和
dvmRawDexFileOpen()一样了,就是对dex文件进行优化和映射。

也只是分析了一个大概流程,还有很多有待之后进行深入。而这里对于阅读Android源码,有了新的体会,首先是工具上,我之前一直是用Source InSight 但是对于一些函数的实现,找起来却是不太方便,因为必须要将函数实现的文件导入到工程中,而用VS来阅读源码,利用Ctrl+Shift+F的功能,在Android源码目录下搜索更为方便,然后可以在Source InSight中进行导入,阅读。其次不得不说阅读源码真的是一个比较痛苦的过程,但真的学习下来,收获还是很大的。

最新文章

  1. mysql 5.6启用强密码
  2. PHP中比较两个时间的大小与日期的差值
  3. Python笔记 001
  4. CSS :hover伪类选择定义和用法
  5. Managing the Lifecycle of a Service
  6. nodejs简易实现一下bigpipe
  7. kinect for windows sdk
  8. ios之&quot;performSelector may cause a leak because its selector is unknown&quot;警告原因及其解决办法
  9. Swift项目兼容Objective-C问题汇总
  10. java 关于extends 和implement的区别
  11. hdu3790最短路径问题 (用优先队列实现的)
  12. 【C语言的日常实践(十六)】字符串输出功能puts、fputs和printf
  13. AngularJS 路由精分
  14. ccse(CountDownLatch,CycliBarrier,Semaplore,Exchanger)
  15. 移动App测试中的最佳做法
  16. LODOP中预览界面查看打印机的可打区域具体值
  17. SQL Server2008及以上 表分区操作详解
  18. 搜索入门_简单搜索bfs dfs大杂烩
  19. 微服务之springCloud-docker-feign-hystrix-ribbon(七)
  20. SQL中的LIKE中用参数化查询

热门文章

  1. Python: 函数参数小结
  2. python: 命令选项及归类
  3. libmemcached upcoming ISO C++ standard, C++0x
  4. SQL 执行顺序
  5. CRC校验算法
  6. layoutSubviews 浅尝
  7. 微软职位内部推荐-Senior Software Engineer -Web
  8. To add private variable to this Javascript literal object
  9. fstream对象重复使用时注意clear()的调用
  10. Week8 软件规格说明书