Android解析ClassLoader(二)Android中的ClassLoader

前言

在上一篇文章我們學(xué)習(xí)了Java的ClassLoader,很多同學(xué)會(huì)把Java和Android的ClassLoader搞混,甚至?xí)J(rèn)為Android中的ClassLoader和Java中的ClassLoader是一樣的,這顯然是不對(duì)的。這一篇文章我們就來學(xué)習(xí)Android中的ClassLoader,來看看它和Java中的ClassLoader有何不同。

1.ClassLoader的類型

我們知道Java中的ClassLoader可以加載jar文件和Class文件(本質(zhì)是加載Class文件),這一點(diǎn)在Android中并不適用,因?yàn)闊o論是DVM還是ART它們加載的不再是Class文件,而是dex文件,這就需要重新設(shè)計(jì)ClassLoader相關(guān)類,我們先來學(xué)習(xí)ClassLoader的類型。
Android中的ClassLoader類型和Java中的ClassLoader類型類似,也分為兩種類型,分別是系統(tǒng)ClassLoader和自定義ClassLoader。其中系統(tǒng)ClassLoader包括三種分別是BootClassLoader、PathClassLoader和DexClassLoader。

1.1 BootClassLoader

Android系統(tǒng)啟動(dòng)時(shí)會(huì)使用BootClassLoader來預(yù)加載常用類,與Java中的BootClassLoader不同,它并不是由C/C++代碼實(shí)現(xiàn),而是由Java實(shí)現(xiàn)的,BootClassLoade的代碼如下所示。
libcore/ojluni/src/main/java/java/lang/ClassLoader.java

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是ClassLoader的內(nèi)部類,并繼承自ClassLoader。BootClassLoader是一個(gè)單例類,需要注意的是BootClassLoader的訪問修飾符是默認(rèn)的,只有在同一個(gè)包中才可以訪問,因此我們?cè)趹?yīng)用程序中是無法直接調(diào)用的。

1.2 DexClassLoader

DexClassLoader可以加載dex文件以及包含dex的壓縮文件(apk和jar文件),不管是加載哪種文件,最終都是要加載dex文件,為了方便理解和敘述,將dex文件以及包含dex的壓縮文件統(tǒng)稱為dex相關(guān)文件。
來查看DexClassLoader的代碼,如下所示。
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java

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

DexClassLoader的構(gòu)造方法有四個(gè)參數(shù):

  • dexPath:dex相關(guān)文件路徑集合,多個(gè)路徑用文件分隔符分隔,默認(rèn)文件分隔符為‘:’
  • optimizedDirectory:解壓的dex文件存儲(chǔ)路徑,這個(gè)路徑必須是一個(gè)內(nèi)部存儲(chǔ)路徑,一般情況下使用當(dāng)前應(yīng)用程序的私有路徑:/data/data/<Package Name>/...
  • librarySearchPath:包含 C/C++ 庫的路徑集合,多個(gè)路徑用文件分隔符分隔分割,可以為null。
  • parent:父加載器。

DexClassLoader 繼承自BaseDexClassLoader ,方法實(shí)現(xiàn)都在BaseDexClassLoader中。

1.3 PathClassLoader

Android系統(tǒng)使用PathClassLoader來加載系統(tǒng)類和應(yīng)用程序的類,來查看它的代碼:
libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java

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

PathClassLoader繼承自BaseDexClassLoader,實(shí)現(xiàn)也都在BaseDexClassLoader中。

PathClassLoader的構(gòu)造方法中沒有參數(shù)optimizedDirectory,這是因?yàn)镻athClassLoader已經(jīng)默認(rèn)了參數(shù)optimizedDirectory的值為:/data/dalvik-cache,很顯然PathClassLoader無法定義解壓的dex文件存儲(chǔ)路徑,因此PathClassLoader通常用來加載已經(jīng)安裝的apk的dex文件(安裝的apk的dex文件會(huì)存儲(chǔ)在/data/dalvik-cache中)。

2.ClassLoader的繼承關(guān)系

運(yùn)行一個(gè)Android程序需要用到幾種類型的類加載器呢?如下所示。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ClassLoader loader = MainActivity.class.getClassLoader();
        while (loader != null) {
            Log.d("liuwangshu",loader.toString());//1
            loader = loader.getParent();
        }
    }
}

首先我們得到MainActivity的類加載器,并在注釋1處通過Log打印出來,接著打印出當(dāng)前類的類加載器的父加載器,直到?jīng)]有父加載器終止循環(huán)。打印結(jié)果如下所示。

10-07 07:23:02.835 8272-8272/? D/liuwangshu: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.liuwangshu.moonclassloader-2/base.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.example.liuwangshu.moonclassloader-2/lib/x86, /vendor/lib, /system/lib]]]
10-07 07:23:02.835 8272-8272/? D/liuwangshu: java.lang.BootClassLoader@e175998

可以看到有兩種類加載器,一種是PathClassLoader,另一種則是BootClassLoader。DexPathList中包含了很多apk的路徑,其中/data/app/com.example.liuwangshu.moonclassloader-2/base.apk就是示例應(yīng)用安裝在手機(jī)上的位置。關(guān)于DexPathList后續(xù)文章會(huì)進(jìn)行介紹。

和Java中的ClassLoader一樣,雖然系統(tǒng)所提供的類加載器有3種類型,但是系統(tǒng)提供的ClassLoader相關(guān)類卻不只3個(gè)。ClassLoader的繼承關(guān)系如下圖所示。



可以看到上面一共有7個(gè)ClassLoader相關(guān)類,其中有一些和Java中的ClassLoader相關(guān)類十分類似,下面簡(jiǎn)單對(duì)它們進(jìn)行介紹:

  • ClassLoader是一個(gè)抽象類,其中定義了ClassLoader的主要功能。BootClassLoader是它的內(nèi)部類。
  • SecureClassLoader類和JDK8中的SecureClassLoader類的代碼是一樣的,它繼承了抽象類ClassLoader。SecureClassLoader并不是ClassLoader的實(shí)現(xiàn)類,而是拓展了ClassLoader類加入了權(quán)限方面的功能,加強(qiáng)了ClassLoader的安全性。
  • URLClassLoader類和JDK8中的URLClassLoader類的代碼是一樣的,它繼承自SecureClassLoader,用來通過URl路徑從jar文件和文件夾中加載類和資源。
  • BaseDexClassLoader繼承自ClassLoader,是抽象類ClassLoader的具體實(shí)現(xiàn)類,PathClassLoader和DexClassLoader都繼承它。

3.BootClassLoader的創(chuàng)建

BootClassLoader是在何時(shí)被創(chuàng)建的呢?這得先從Zygote進(jìn)程開始說起,不了解Zygote進(jìn)程的可以查看Android系統(tǒng)啟動(dòng)流程(二)解析Zygote進(jìn)程這篇文章。
ZygoteInit的main方法如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

 public static void main(String argv[]) {
   ...
        try {
             ...
                preload(bootTimingsTraceLog);
             ... 
        }
    }

main方法是ZygoteInit入口方法,其中調(diào)用了ZygoteInit的preload方法,preload方法中又調(diào)用了ZygoteInit的preloadClasses方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

 private static void preloadClasses() {
        final VMRuntime runtime = VMRuntime.getRuntime();
        InputStream is;
        try {
            is = new FileInputStream(PRELOADED_CLASSES);//1
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
            return;
        }
        ...
        try {
            BufferedReader br
                = new BufferedReader(new InputStreamReader(is), 256);//2

            int count = 0;
            String line;
            while ((line = br.readLine()) != null) {//3
                line = line.trim();
                if (line.startsWith("#") || line.equals("")) {
                    continue;
                }
                  Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                try {
                    if (false) {
                        Log.v(TAG, "Preloading " + line + "...");
                    }
                    Class.forName(line, true, null);//4
                    count++;
                } catch (ClassNotFoundException e) {
                    Log.w(TAG, "Class not found for preloading: " + line);
                } 
        ...
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
        } finally {
            ...
        }
    }

preloadClasses方法用于Zygote進(jìn)程初始化時(shí)預(yù)加載常用類。注釋1處將/system/etc/preloaded-classes文件封裝成FileInputStream,preloaded-classes文件中存有預(yù)加載類的目錄,這個(gè)文件在系統(tǒng)源碼中的路徑為frameworks/base/preloaded-classes,這里列舉一些preloaded-classes文件中的預(yù)加載類名稱,如下所示。

android.app.ApplicationLoaders
android.app.ApplicationPackageManager
android.app.ApplicationPackageManager$OnPermissionsChangeListenerDelegate
android.app.ApplicationPackageManager$ResourceName
android.app.ContentProviderHolder
android.app.ContentProviderHolder$1
android.app.ContextImpl
android.app.ContextImpl$ApplicationContentResolver
android.app.DexLoadReporter
android.app.Dialog
android.app.Dialog$ListenersHandler
android.app.DownloadManager
android.app.Fragment

可以看到preloaded-classes文件中的預(yù)加載類的名稱有很多都是我們非常熟知的。預(yù)加載屬于拿空間換時(shí)間的策略,Zygote環(huán)境配置的越健全越通用,應(yīng)用程序進(jìn)程需要單獨(dú)做的事情也就越少,預(yù)加載除了預(yù)加載類,還有預(yù)加載資源和預(yù)加載共享庫,因?yàn)椴皇潜疚闹攸c(diǎn),這里就不在延伸講下去了。
回到preloadClasses方法的注釋2處,將FileInputStream封裝為BufferedReader,并注釋3處遍歷BufferedReader,讀出所有預(yù)加載類的名稱,每讀出一個(gè)預(yù)加載類的名稱就調(diào)用注釋4處的代碼加載該類,Class的forName方法如下所示。
libcore/ojluni/src/main/java/java/lang/Class.java

    @CallerSensitive
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            loader = BootClassLoader.getInstance();//1
        }
        Class<?> result;
        try {
            result = classForName(name, initialize, loader);//2
        } catch (ClassNotFoundException e) {
            Throwable cause = e.getCause();
            if (cause instanceof LinkageError) {
                throw (LinkageError) cause;
            }
            throw e;
        }
        return result;
    }

注釋1處創(chuàng)建了BootClassLoader,并將BootClassLoader實(shí)例傳入到了注釋2處的classForName方法中,classForName方法是Native方法,它的實(shí)現(xiàn)由c/c++代碼來完成,如下所示。

    @FastNative
    static native Class<?> classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;

4.PathClassLoader的創(chuàng)建

PathClassLoader的創(chuàng)建也得從Zygote進(jìn)程開始說起,Zygote進(jìn)程啟動(dòng)SyetemServer進(jìn)程時(shí)會(huì)調(diào)用ZygoteInit的startSystemServer方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

private static boolean startSystemServer(String abiList, String socketName)
           throws MethodAndArgsCaller, RuntimeException {
    ...
        int pid;
        try {
            parsedArgs = new ZygoteConnection.Arguments(args);//2
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
            /*1*/
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
       if (pid == 0) {//2
           if (hasSecondZygote(abiList)) {
               waitForSecondaryZygote(socketName);
           }
           handleSystemServerProcess(parsedArgs);//3
       }
       return true;
   }

注釋1處,Zygote進(jìn)程通過forkSystemServer方法fork自身創(chuàng)建子進(jìn)程(SystemServer進(jìn)程)。注釋2處如果forkSystemServer方法返回的pid等于0,說明當(dāng)前代碼是在新創(chuàng)建的SystemServer進(jìn)程中執(zhí)行的,接著就會(huì)執(zhí)行注釋3處的handleSystemServerProcess方法:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

 private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws Zygote.MethodAndArgsCaller {

    ...
        if (parsedArgs.invokeWith != null) {
           ...
        } else {
            ClassLoader cl = null;
            if (systemServerClasspath != null) {
                cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);//1
                Thread.currentThread().setContextClassLoader(cl);
            }
            ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
        }
    }

注釋1處調(diào)用了createPathClassLoader方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

  static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
      String libraryPath = System.getProperty("java.library.path");
      return PathClassLoaderFactory.createClassLoader(classPath,
                                                      libraryPath,
                                                      libraryPath,
                                                      ClassLoader.getSystemClassLoader(),
                                                      targetSdkVersion,
                                                      true /* isNamespaceShared */);
    }

createPathClassLoader方法中又會(huì)調(diào)用PathClassLoaderFactory的createClassLoader方法,看來PathClassLoader是用工廠來進(jìn)行創(chuàng)建的。
frameworks/base/core/java/com/android/internal/os/PathClassLoaderFactory.java

  public static PathClassLoader createClassLoader(String dexPath,
                                                    String librarySearchPath,
                                                    String libraryPermittedPath,
                                                    ClassLoader parent,
                                                    int targetSdkVersion,
                                                    boolean isNamespaceShared) {
        PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
      ...
        return pathClassloader;
    }

在PathClassLoaderFactory的createClassLoader方法中會(huì)創(chuàng)建PathClassLoader。

結(jié)語

在這篇文章中我們學(xué)習(xí)了Android的ClassLoader的類型、ClassLoader的繼承關(guān)系以及BootClassLoader和PathClassLoader是何時(shí)創(chuàng)建的。BootClassLoader是在Zygote進(jìn)程的入口方法中創(chuàng)建的,PathClassLoader則是在Zygote進(jìn)程創(chuàng)建SystemServer進(jìn)程時(shí)創(chuàng)建的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容