Android進階(七)Android中的ClassLoader

1、Java中的ClassLoader

一、繼承關係

  • ClassLoader是一個抽象類,其中定義了ClassLoader的主要功能。
  • SecureClassLoader繼承了抽象類ClassLoader,但SecureClassLoader並非ClassLoader的實現類,而是拓展了ClassLoader類加入了權限方面的功能,增強了ClassLoader的安全性。
  • URLClassLoader繼承自SecureClassLoader,用來經過URl路徑從jar文件和文件夾中加載類和資源。
  • ExtClassLoader和AppClassLoader都繼承自URLClassLoader,它們都是Launcher 的內部類,Launcher 是Java虛擬機的入口應用,ExtClassLoader和AppClassLoader都是在Launcher中進行初始化的。

2、Android中的ClassLoader

一、繼承關係

  • ClassLoader是一個抽象類,其中定義了ClassLoader的主要功能。BootClassLoader是它的內部類。
  • SecureClassLoader繼承了抽象類ClassLoader。SecureClassLoader並非ClassLoader的實現類,而是拓展了ClassLoader類加入了權限方面的功能,增強了ClassLoader的安全性。
  • URLClassLoader類繼承自SecureClassLoader,用來經過URl路徑從jar文件和文件夾中加載類和資源。
  • InMemoryDexClassLoader是Android8.0新增的類加載器,繼承自BaseDexClassLoader,用於加載內存中的dex文件。
  • BaseDexClassLoader繼承自ClassLoader,是抽象類ClassLoader的具體實現類,PathClassLoader和DexClassLoader都繼承它。

二、做用和分類

(1)做用:
Android虛擬機運行的是Dex字節碼(將Class文件合併優化生成的產物),ClassLoader用來加載dex文件。java

(2)分類
分爲系統類加載器和自定義類加載器。主要的系統類加載器:android

  • BootClassLoader
  • PathClassLoader
  • DexClassLoader

3、Android中ClassLoader源碼分析

相關源碼:
/dalvik/systemapi

一、ClassLoader

(1)構造函數:數組

protected ClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
複製代碼

ClassLoader須要傳入一個父ClassLoader,若是父ClassLoader不傳的時候,經過 getSystemClassLoader方法,建立了一個PathClassLoader安全

public static ClassLoader getSystemClassLoader() {
    return SystemClassLoader.loader;
}
  static private class SystemClassLoader {
    public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
private static ClassLoader createSystemClassLoader() {
    String classPath = System.getProperty("java.class.path", ".");
    String librarySearchPath = System.getProperty("java.library.path", "");
    return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
複製代碼

(2)loadClass方法:bash

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
    // First, check if the class has already been loaded
    //(1)先判斷這個類是否被加載過
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            //(2)若是沒被加載,先讓父加載器進行加載
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // ClassNotFoundException thrown if class not found
            // from the non-null parent class loader
        }
        //(3)若是父加載器加載失敗,則自身進行加載
        if (c == null) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    return c;
}
複製代碼

雙親委託機制:
(1)除了頂層的類加載器外,其餘的類加載器都有本身的父類加載器,在加載類時首先判斷這個類是否被加載過,若是已經加載則直接返回。
(2)若是未被加載過,則先嚐試讓父加載器進行加載,最終全部加載請求都會傳遞給頂層的加載器中。
(3)當父加載器發現未找到所需的類而沒法完成加載請求時,子加載器的findClass方法中進行加載。ide

二、BaseDexClassLoader

(1)構造:函數

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    this(dexPath, librarySearchPath, parent, null, false);
}

public BaseDexClassLoader(String dexPath,
        String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
        boolean isTrusted) {
    super(parent);
    this.sharedLibraryLoaders = sharedLibraryLoaders == null
            ? null
            : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    if (reporter != null) {
        reportClassLoaderChain();
    }
}
複製代碼
  • dexPath:指目標類所在的APK或jar文件的路徑,若是要包含多個路徑,路徑之間必須使用特定的分割符分隔,特定的分割符可使用System.getProperty(「path.separtor」)得到。
  • optimizedDirectory:解壓的dex文件存儲路徑,這個路徑必須是一個內部存儲路徑,通常狀況下使用當前應用程序的私有路徑:/data/data//...。
  • librarySearchPath:指目標類中所使用的C/C++庫存放的路徑
  • parent:父加載器。

(2)DexPathList
DexPathList中維護着一個Element數組,這個數組中Element元素就是Dex文件源碼分析

#DexPathList
 /*package*/ static class Element {
 
    @UnsupportedAppUsage
    private final File path;
    /** Whether {@code path.isDirectory()}, or {@code null} if {@code path == null}. */
    private final Boolean pathIsDirectory;
    @UnsupportedAppUsage
    private final DexFile dexFile;
    private ClassPathURLStreamHandler urlHandler;
    private boolean initialized;
    
    @UnsupportedAppUsage
    public Element(DexFile dexFile, File dexZipPath) {
        if (dexFile == null && dexZipPath == null) {
            throw new NullPointerException("Either dexFile or path must be non-null");
        }
        this.dexFile = dexFile;
        this.path = dexZipPath;
        // Do any I/O in the constructor so we don't have to do it elsewhere, eg. toString(). this.pathIsDirectory = (path == null) ? null : path.isDirectory(); } public Element(DexFile dexFile) { this(dexFile, null); } public Element(File path) { this(null, path); } ...... } 複製代碼

三、BootClassLoader

Android平臺上全部ClassLoader的最終parent。Android系統啓動時會使用BootClassLoader來預加載經常使用類。優化

class BootClassLoader extends ClassLoader {
    private static BootClassLoader instance;
    @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
    public static synchronized BootClassLoader getInstance() {
        if (instance == null) {
            instance = new BootClassLoader();
        }
        return instance;
    }
...
}
複製代碼

BootClassLoader的訪問修飾符是默認的,只有在同一個包中才能夠訪問

四、PathClassLoader

public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
}

@libcore.api.CorePlatformApi
public PathClassLoader(
        String dexPath, String librarySearchPath, ClassLoader parent,
        ClassLoader[] sharedLibraryLoaders) {
    super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
}
複製代碼
  • 使用PathClassLoader來加載系統類和應用程序的類,一般用來加載已安裝的apk的dex文件
  • 構造函數沒有optimizedDirectory,沒法定義dex文件路徑,該參數默認值爲/data/dalvik-cache

五、DexClassLoader

public DexClassLoader(String dexPath, String optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
}
複製代碼

DexClassLoader能夠加載dex文件以及包含dex的壓縮文件(apk和jar文件),在其父類BaseDexClassLoader裏對".jar",".zip",".apk",".dex"後綴的文件最後都會生成一個對應的dex文件。

4、Android加載Class的過程

一、BaseDexClassLoader的findClass

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    // First, check whether the class is present in our shared libraries.
    if (sharedLibraryLoaders != null) {
        for (ClassLoader loader : sharedLibraryLoaders) {
            try {
                return loader.loadClass(name);
            } catch (ClassNotFoundException ignored) {
            }
        }
    }
    // Check whether the class in question is present in the dexPath that
    // this classloader operates on.
    List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException(
                "Didn't find class \"" + name + "\" on path: " + pathList);
        for (Throwable t : suppressedExceptions) {
            cnfe.addSuppressed(t);
        }
        throw cnfe;
    }
    return c;
}
複製代碼

內部調用了DexPathList的findClass方法

#DexPathList
private Element[] dexElements;
......
public Class<?> findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        Class<?> clazz = element.findClass(name, definingContext, suppressed);
        if (clazz != null) {
            return clazz;
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

public Class<?> findClass(String name, ClassLoader definingContext,
        List<Throwable> suppressed) {
    return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
            : null;
}
複製代碼

DexPathList內部包含了一個DexFile的數組dexElements。類加載的過程,就是遍歷這個數組調用了DexFile的loadClassBinaryName方法,最終調用native方法defineClassNative。

參考資料:
Android動態加載之ClassLoader詳解

相關文章
相關標籤/搜索