基础概念

  • Android中的ClassLoader类型可分为系统ClassLoader自定义ClassLoader。其中系统ClassLoader包括3种分别是:

    • BootClassLoader,Android系统启动时会使用BootClassLoader来预加载常用类,与Java中的Bootstrap ClassLoader不同的是,它并不是由C/C++代码实现,而是由Java实现的。BootClassLoaderClassLoader的一个内部类。

      • PathClassLoader,只能加载系统中已经安装过的apk。
      • DexClassLoader,是一个可以从包含classes.dex实体的.jar或.apk文件中加载classes的类加载器。可以用于实现dex的动态加载、代码热更新等等。可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。
      • PathClassLoaderDexClasLoader都是继承自 BaseDexClassLoader,它们的类加载逻辑全部写在BaseDexClassLoader中。

源码分析

DexClassLoader分析

DexClassLoader.java

位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
继承BaseDexClassLoader
public class DexClassLoader extends BaseDexClassLoader {       public DexClassLoader(String dexPath, String optimizedDirectory,            String libraryPath, ClassLoader parent) {        super(dexPath, new File(optimizedDirectory), libraryPath, parent);    }}

参数:

  • dexpath:jar或apk文件目录
  • optimizedDirectory:优化dex缓存目录
  • libraryPath:包含native lib的目录路径
  • parent:父类加载器

BaseDexClassLoader.java

位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
继承ClassLoader
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);    }    ...}

首先调用父类的构造函数super(parent):ClassLoader
然后调用并初始化了DexPathList类型的对象pathList。

ClassLoader.java

位置:~/android6.0.1_r66/libcore/libart/src/main/java/java/lang/ClassLoader.java
public abstract class ClassLoader {...protected ClassLoader(ClassLoader parentLoader) {        this(parentLoader, false);    }ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {        if (parentLoader == null && !nullAllowed) {            throw new NullPointerException("parentLoader == null && !nullAllowed");        }        parent = parentLoader;    }...}

该构造函数把传进来的父类加载器赋给了私有变量parent。

DexPathList.java

位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
final class DexPathList {    private static final String DEX_SUFFIX = ".dex";    private static final String zipSeparator = "!/";        ...        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;        ArrayList suppressedExceptions = new ArrayList();        // save dexPath for BaseDexClassLoader        this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,                                            suppressedExceptions);                                             this.nativeLibraryDirectories = splitPaths(libraryPath, false);        this.systemNativeLibraryDirectories =                splitPaths(System.getProperty("java.library.path"), true);        List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);                allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null,                                                          suppressedExceptions);        if (suppressedExceptions.size() > 0) {            this.dexElementsSuppressedExceptions =                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);        } else {            dexElementsSuppressedExceptions = null;        }    }}

首先对传入参数的验证,然后调用makeDexElements(List files, File optimizedDirectory, List suppressedExceptions, ClassLoader loader)方法

private static Element[] makePathElements(List files, File optimizedDirectory,                                              List suppressedExceptions) {        List elements = new ArrayList<>();        /*         * Open all files and load the (direct or contained) dex files         * up front.         */        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()) {                // We support directories for looking up resources and native libraries.                // Looking up resources in directories is useful for running libcore tests.                elements.add(new Element(file, true, null, null));            } else if (file.isFile()) {                if (name.endsWith(DEX_SUFFIX)) {//原始的dex文件处理,而不是在zip或者jar中的dex文件                                      try {                        dex = loadDexFile(file, optimizedDirectory);                    } catch (IOException ex) {                        System.logE("Unable to load dex file: " + file, ex);                    }                } else {//处理zip或者jar包中的dex文件                    zip = file;                    try {                        dex = loadDexFile(file, optimizedDirectory);                    } catch (IOException suppressed) {                        /*                         * IOException might get thrown "legitimately" by the DexFile constructor if                         * the zip file turns out to be resource-only (that is, no classes.dex file                         * in it).                         * Let dex == null and hang on to the exception to add to the tea-leaves for                         * when findClass returns null.                         */                        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()]);    }

dex文件、zip或者jar包中的dex文件都会调用loadDexFile

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);        }    }

如果optimizedDirectory为null就会新建一个DexFile对象,调用DexFile的构造函数。

位置:~/android6.0.1_r66/libcore/dalvik/src/main/java/dalvik/system/DexFile.java
... public DexFile(File file) throws IOException {        this(file.getPath());    } public DexFile(String fileName) throws IOException {//fileName就是上一个构造函数的file.getPath()        mCookie = openDexFile(fileName, null, 0);        mFileName = fileName;        guard.open("close");}    ...

关键函数是openDexFile。

private static Object 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);    }

openDexFileNative是一个native函数,需要找到具体实现:
grep -rns:-r指定要查找的是目录 -n显示行号 -s不显示错误信息



所以openDexFileNative的具体实现就是:

位置:~/android6.0.1_r66/art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(    JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {  ScopedUtfChars sourceName(env, javaSourceName);  if (sourceName.c_str() == nullptr) {    return 0;  }  NullableScopedUtfChars outputName(env, javaOutputName);  if (env->ExceptionCheck()) {    return 0;  }  ClassLinker* linker = Runtime::Current()->GetClassLinker();  std::vector> dex_files;  std::vector error_msgs;  dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs);  if (!dex_files.empty()) {    jlongArray array = ConvertNativeToJavaArray(env, dex_files);    if (array == nullptr) {      ScopedObjectAccess soa(env);      for (auto& dex_file : dex_files) {        if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {          dex_file.release();        }      }    }    return array;  } else {    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 nullptr;  }}

更多相关文章

  1. 在Android中如何让gif动起来
  2. 【ncnn android】算法移植(九)——DBface android移植
  3. Android的IPC机制Binder的详解汇总
  4. android 静音与振动
  5. Cordova 3.x 基础(13) -- 为Android(安卓)APK签名
  6. Android文件系统的结构及目录用途、操作方法 整理
  7. 关于android的各种disk images(看过的讲android image比较细致的
  8. Android(安卓)JNI详述(二)
  9. Android(安卓)SDK等下载说明

随机推荐

  1. android HAL 详解
  2. Android多线程:理解和简单使用总结
  3. Android之怎么使用SQLite数据库(增、删、
  4. Android(安卓)开发工具集合 - (Android(
  5. Android 2.2 源码结构分析
  6. Webview如何触发onReceivedLoginRequest;W
  7. Android(安卓)切换卡(TabWidget)
  8. AndroidX介绍及项目迁移
  9. Android P 中的网络安全配置指南 network
  10. FusionCharts报表在Android上的实现