系統(tǒng)ClassLoader相關(guān)及Application初始化簡單分析及總結(jié)

概述

1:ClassLoader相關(guān)知識(shí)
Class文件由類裝載器裝載后,在JVM中將形成一份描述Class結(jié)構(gòu)的元信息對象,通過該元信息對象可以獲知Class的結(jié)構(gòu)信息:如構(gòu)造函數(shù),屬性和方法等,Java允許用戶借由這個(gè)Class相關(guān)的元信息對象間接調(diào)用Class對象的功能。ClassLoader即用于把類的數(shù)據(jù)從class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型。

一個(gè)類的唯一性要由它的類加載器和它本身來確定,也就是說一個(gè)Class文件如果使用不同的類加載器來加載,那么加載出來的類也是不相等的,而在Java中為了保證一個(gè)類的唯一性使用了雙親委派模型,也就是說如果要加載一個(gè)類首先會(huì)委托給自己的父加載器去完成,父加載器會(huì)再向上委托,直到最頂層的類加載器;如果所有向上父加載器沒有找到這個(gè)要加載的類,子類才會(huì)嘗試自己去加載,這樣就保證了加載的類都是一個(gè)類,例如Object都是一個(gè)類。java中的類加載器主要有:BootstrapClassLoader、ExtensionClassLoader 及 ApplicationClassLoader


172124489257909.png

2:Android中的ClassLoader:

1437930-bb9d359f4c7e9935.png

Androroid 中所有類加載器均為java.lang.ClassLoader的子類,主要有2種構(gòu)造方法,一種為支持傳入父加載器,一種為無參構(gòu)造方法,而無參構(gòu)造方法,傳入的默認(rèn)父加載器為getSystemClassLoader代碼如下

package java.lang;

public abstract class ClassLoader {
 protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
  @CallerSensitive
    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", "");

        // String[] paths = classPath.split(":");
        // URL[] urls = new URL[paths.length];
        // for (int i = 0; i < paths.length; i++) {
        // try {
        // urls[i] = new URL("file://" + paths[i]);
        // }
        // catch (Exception ex) {
        // ex.printStackTrace();
        // }
        // }
        //
        // return new java.net.URLClassLoader(urls, null);

        // TODO Make this a java.net.URLClassLoader once we have those?
        return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
    }
  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;
    }

    public BootClassLoader() {
        super(null, true);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return Class.classForName(name, false, null);
    }

    @Override
    protected URL findResource(String name) {
        return VMClassLoader.getResource(name);
    }

    @SuppressWarnings("unused")
    @Override
    protected Enumeration<URL> findResources(String resName) throws IOException {
        return Collections.enumeration(VMClassLoader.getResources(resName));
    }

    /**
     * Returns package information for the given package. Unfortunately, the
     * Android BootClassLoader doesn't really have this information, and as a
     * non-secure ClassLoader, it isn't even required to, according to the spec.
     * Yet, we want to provide it, in order to make all those hopeful callers of
     * {@code myClass.getPackage().getName()} happy. Thus we construct a Package
     * object the first time it is being requested and fill most of the fields
     * with dummy values. The Package object is then put into the ClassLoader's
     * Package cache, so we see the same one next time. We don't create Package
     * objects for null arguments or for the default package.
     * <p>
     * There a limited chance that we end up with multiple Package objects
     * representing the same package: It can happen when when a package is
     * scattered across different JAR files being loaded by different
     * ClassLoaders. Rather unlikely, and given that this whole thing is more or
     * less a workaround, probably not worth the effort.
     */
    @Override
    protected Package getPackage(String name) {
        if (name != null && !name.isEmpty()) {
            synchronized (this) {
                Package pack = super.getPackage(name);

                if (pack == null) {
                    pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0",
                            "Unknown", null);
                }

                return pack;
            }
        }

        return null;
    }

    @Override
    public URL getResource(String resName) {
        return findResource(resName);
    }

    @Override
    protected Class<?> loadClass(String className, boolean resolve)
           throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(className);

        if (clazz == null) {
            clazz = findClass(className);
        }

        return clazz;
    }

    @Override
    public Enumeration<URL> getResources(String resName) throws IOException {
        return findResources(resName);
    }
}
}

由以上可知
2.1BootClassLoader 為所有android加載器中最頂層加載器,與jvm的最頂層加載器BootstrapClassLoader由C++實(shí)現(xiàn)不同,其為java實(shí)現(xiàn),為ClassLoader的內(nèi)部類。可通過以上
ClassLoader.getSystemClassLoader().getParent()獲取

2.2 PathClassLoader 繼承自BaseDexClassLoader ,它是我們apk的默認(rèn)加載器,它是用來加載系統(tǒng)類和主dex文件中的類的,但是系統(tǒng)類是由BootClassLoader加載的,如果apk中有多個(gè)dex文件,只會(huì)加載主dex

2.3 DexClassLoader繼承自BaseDexClassLoader ,可以用來加載外置的dex文件或者apk等
Android中主要使用的ClassLoader有PathClassLoader和DexClassLoader,它們都繼承自BaseDexClassLoader,BaseDexClassLoader中維護(hù)了一個(gè)DexPathList,PathClassLoader和DexClassLoader查找類的操作直接調(diào)用BaseClassLoader的findClass方法,而BaseClassLoader的findClass中又通過內(nèi)部維護(hù)的DexPathList來查找,DexPathList中又維護(hù)這一個(gè)Element數(shù)組,這個(gè)數(shù)組中Element元素其實(shí)就是Dex文件。

PathClassLoader和DexClassLoader最大的區(qū)別就是DexClassLoader可以加載外置dex文件,這是因?yàn)镻athClassLoader構(gòu)造方法中像上傳遞時(shí)第二個(gè)參數(shù)傳了null,這個(gè)參數(shù)代表的是dex優(yōu)化后的路徑,DexPathList在生成Element數(shù)組時(shí)會(huì)判斷這個(gè)參數(shù)是否為null,如果為null就使用系統(tǒng)默認(rèn)路徑/data/dalvik-cache,這也是導(dǎo)致如果要加載外置dex文件只能使用DexClassLoader的原因。

PathClassLoader只會(huì)加載apk中的主dex文件,其他的dex文件是使用DexClassloader動(dòng)態(tài)加載進(jìn)來,然后通過反射獲取到PathClassLoader中的DexPathList,然后再拿到DexPathList中的Element數(shù)組,最后將后加載進(jìn)來的dex和反射拿到的數(shù)組進(jìn)行合并后并重新設(shè)置回去,這也是Google的MultiDex的做法。
``
總結(jié)完class loader,接下來分析Application創(chuàng)建過程(分析的源碼基于21).

Andoriod應(yīng)用zygote進(jìn)程中fork后,首先會(huì)創(chuàng)建一個(gè)屬于自己的進(jìn)程,在進(jìn)程創(chuàng)建后會(huì)調(diào)用ActivityThread中的main方法,在main方法中會(huì)開啟消息循環(huán)并和AMS綁定,然后AMS會(huì)調(diào)用ActivityThread中的bindApplication方法,這個(gè)方法發(fā)送了一個(gè)消息到Handler中并調(diào)用handleBindApplication方法開始創(chuàng)建Application,也代表了一個(gè)應(yīng)用程序真正的啟動(dòng)了,就從這個(gè)方法開始,所以頂端加載主dex的方法入口也在這。
frameworks/base/core/java/android/app/ActivityThread.java

 static final class AppBindData {
        LoadedApk info;
       。。。
    }
//-------------------**********************我是入口***************----------------------
private void handleBindApplication(AppBindData data) {

    。。。。
    //創(chuàng)建LoaderApk
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);

    。。。。

    try {
        //調(diào)用了LoadedApk中的makeApplication方法創(chuàng)建Application,也就是我們應(yīng)用中的Application 
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);

        。。。。
    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
//--------------------我是b----------------------->
   public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
            CompatibilityInfo compatInfo) {
        return getPackageInfo(ai, compatInfo, null, false, true, false);
    }
//--------------------我是c----------------------->

//上面b中傳入的第3個(gè)參數(shù)是null,也就是說這里的ClassLoader是null

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
        ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
        boolean registerPackage) {
    synchronized (mResourcesManager) {
        //嘗試從緩存中獲取
        WeakReference<LoadedApk> ref;
        if (includeCode) {
            ref = mPackages.get(aInfo.packageName);
        } else {
            ref = mResourcePackages.get(aInfo.packageName);
        }           
        LoadedApk packageInfo = ref != null ? ref.get() : null;

        //未命中緩存 或者 有資源且資源管理器AssetManage未過期
        if (packageInfo == null || (packageInfo.mResources != null
                && !packageInfo.mResources.getAssets().isUpToDate())) {
            //直接創(chuàng)建一個(gè)LoadedApk,傳入了ClassLoader,但是上面?zhèn)魅氲氖莕ull
            packageInfo =
                new LoadedApk(this, aInfo, compatInfo, baseLoader,
                        securityViolation, includeCode &&
                        (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != , registerPackage);

            //如果是系統(tǒng)進(jìn)程
            if (mSystemThread && "android".equals(aInfo.packageName)) {
                packageInfo.installSystemApplicationInfo(aInfo,
                        getSystemContext().mPackageInfo.getClassLoader());
            }
            //存入緩存
            if (includeCode) {
                mPackages.put(aInfo.packageName,
                        new WeakReference<LoadedApk>(packageInfo));
            } else {
                mResourcePackages.put(aInfo.packageName,
                        new WeakReference<LoadedApk>(packageInfo));
            }
        }
        return packageInfo;
    }
}
}

入口為handleBindApplication方法,先調(diào)用了getPackageInfoNoCheck該方法即調(diào)用getPackageInfo,從緩存中獲取名為packageInfoLoadedApk,沒有則創(chuàng)建一個(gè)新的。
frameworks/base/core/java/android/app/LoadedApk.java

public final class LoadedApk {
   public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
        CompatibilityInfo compatInfo, ClassLoader baseLoader,
        boolean securityViolation, boolean includeCode, boolean registerPackage) {

    mActivityThread = activityThread;
    setApplicationInfo(aInfo);
    mPackageName = aInfo.packageName;
    //將傳入的ClassLoader賦值給了mBaseClassLoader,上面?zhèn)魅氲臑閚ull
    mBaseClassLoader = baseLoader;
    mSecurityViolation = securityViolation;
    mIncludeCode = includeCode;
    mRegisterPackage = registerPackage;
    mDisplayAdjustments.setCompatibilityInfo(compatInfo);
}
//---------------------------------------------------------------------->
 public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {

    //保證只創(chuàng)建一次Application    
    if (mApplication != null) {
        return mApplication;
    }
  
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

    Application app = null;
   。。。。

    try {
        //獲取ClassLoader
        java.lang.ClassLoader cl = getClassLoader();
        //不是系統(tǒng)包名
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            //不是系統(tǒng)應(yīng)用執(zhí)行了initializeJavaContextClassLoader     
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        //創(chuàng)建Context,這個(gè)由于系統(tǒng)版本不同可能是ContextImpl或者ApplicationContext
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

        //創(chuàng)建Application
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        if (!mActivityThread.mInstrumentation.onException(app, e)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + ": " + e.toString(), e);
        }
    }
   mActivityThread.mAllApplications.add(app);
   mApplication = app;
    。。。。

    return app;
}   
}

LoadedApk的構(gòu)造方法僅是將傳進(jìn)來的baseLoader賦值給成員變量mBaseClassLoader,由上可知,此時(shí)傳進(jìn)來的baseLoader為null,到此構(gòu)造方法結(jié)束主要進(jìn)行些賦值操作。

回到入口的第2個(gè)方法即LoadedApkmakeApplication.LoadedApk的成員變量
mApplication就是該方法返回的應(yīng)用Application.先進(jìn)行非空判斷,有則直接返回沒有則新建一個(gè)在賦值給成員變量再返回,保證只創(chuàng)建一次。創(chuàng)建context,從而創(chuàng)建Application。但到此時(shí)成員變量baseLoader仍為null,故getClassLoader獲取ClassLoader cl,該方法對ClassLoader應(yīng)該有些操作。

frameworks/base/core/java/android/app/LoadedApk.java

public ClassLoader getClassLoader() {
    synchronized (this) {

        //如果mClassLoader不為空,直接返回了
        if (mClassLoader != null) {
            return mClassLoader;
        }

        if (mIncludeCode && !mPackageName.equals("android")) {
            //不是系統(tǒng)應(yīng)用
           。。。。
            //獲取ClassLoader對象,這里傳入的mBaseClassLoader還是null,因?yàn)長oadedApk創(chuàng)建的時(shí)候傳入的就是null
            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
                    mBaseClassLoader);

            StrictMode.setThreadPolicy(oldPolicy);
        } else {
            //是系統(tǒng)應(yīng)用
            if (mBaseClassLoader == null) {
                mClassLoader = ClassLoader.getSystemClassLoader();
            } else {
                mClassLoader = mBaseClassLoader;
            }
        }
        return mClassLoader;
    }
}

先進(jìn)行非空判斷,在進(jìn)行是否是系統(tǒng)應(yīng)用進(jìn)行不同賦值,我們?yōu)榉窍到y(tǒng)應(yīng)用,故調(diào)用ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); ,mBaseClassLoader為null。

frameworks/base/core/java/android/app/ApplicationLoaders.java

public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent)
{
    //這里獲取的是BootClassLoader  在 2 中代碼可見
    ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();

    synchronized (mLoaders) {

        //parent是LoadedApk剛傳入的mBaseClassLoader,還是null
        if (parent == null) {
            //設(shè)置parent=BootClassLoader
            parent = baseParent;
        }

        //這里肯定相等
        if (parent == baseParent) {
            //嘗試獲取緩存
            ClassLoader loader = mLoaders.get(zip);
            if (loader != null) {
                return loader;
            }

            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);

            //創(chuàng)建PathClassLoader,終于出現(xiàn)了
          PathClassLoader pathClassloader  = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
                        mBaseClassLoader);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
          //存入緩存
            mLoaders.put(zip, pathClassloader);
            return pathClassloader;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
        PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        return pathClassloader;
    }
}

判斷是否非空,空則創(chuàng)建。判斷傳入的classloader 是否為null,null則取系統(tǒng)BootClassLoader作為parent,創(chuàng)建PathClassLoader并賦值mClassLoader。到此PathClassLoader創(chuàng)建完成回到LoadApk.makeApplication方法創(chuàng)建Applicaiton前,非系統(tǒng)應(yīng)用調(diào)用了initializeJavaContextClassLoader.

frameworks/base/core/java/android/app/LoadedApk.java

 private void initializeJavaContextClassLoader() {
    IPackageManager pm = ActivityThread.getPackageManager();
    android.content.pm.PackageInfo pi;
    try {
        pi = pm.getPackageInfo(mPackageName, , UserHandle.myUserId());
    } catch (RemoteException e) {
        throw new IllegalStateException("Unable to get package info for "
                + mPackageName + "; is system dying?", e);
    }
    if (pi == null) {
        throw new IllegalStateException("Unable to get package info for "
                + mPackageName + "; is package not installed?");
    }

    boolean sharedUserIdSet = (pi.sharedUserId != null);
    boolean processNameNotDefault =
        (pi.applicationInfo != null &&
         !mPackageName.equals(pi.applicationInfo.processName));
    boolean sharable = (sharedUserIdSet || processNameNotDefault);
    ClassLoader contextClassLoader =
        (sharable)
        ? new WarningContextClassLoader()
        : mClassLoader;

    //設(shè)置當(dāng)前線程的ClassLoader    
    Thread.currentThread().setContextClassLoader(contextClassLoader);
}

該方法只是將主進(jìn)程進(jìn)程的classloader設(shè)置為創(chuàng)建的PathClassLoader.接下來回到
LoadedApk.makeApplication中Context的創(chuàng)建,系統(tǒng)版本不同,該創(chuàng)建的context可能為ContextImplApplicationContext,21的源碼為ContextImpl,調(diào)用方法為ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo)

frameworks/base/core/java/android/app/ContextImpl.java

  static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
    //直接new了一個(gè)ContextImpl
    return new ContextImpl(null, mainThread,
            packageInfo, null, null, false, null, null);
}

//-----------------------------------
 private ContextImpl(ContextImpl container, ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
        Display display, Configuration overrideConfiguration) {
   。。。。

    //mPackageInfo,將傳入的LoadedApk賦值給了mPackageInfo
    mPackageInfo = packageInfo;

調(diào)用ContextImpl構(gòu)造方法創(chuàng)建對象,將傳入的LoadedApk賦值給了mPackageInfo,接下來回到LoadedApk.makeApplication中最后Applicaiton的創(chuàng)建。

frameworks/base/core/java/android/app/Instrumentation.java

  public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
/**cl為上面創(chuàng)建的 PathClassLoader
 context  為上面創(chuàng)建的ContextImpl 
  className 為 String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }
即我們Androidmenifest指定的Application類名,沒指定則為系統(tǒng)Application

**/


    //使用了ClassLoader.loadClass來加載Application類,這個(gè)ClassLoader就是上面創(chuàng)建的PathClassLoader,這里傳入的context就是上面創(chuàng)建的ContextImpl    
    return newApplication(cl.loadClass(className), context);
}

ClassLoader.loadClass來加載Application類,后直接調(diào)用另外一個(gè)重載方法
frameworks/base/core/java/android/app/Instrumentation.java

static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    //創(chuàng)建Application并回調(diào)  attach方法
    Application app = (Application)clazz.newInstance();
    //調(diào)用Application的attach方法,傳入的context還是上面創(chuàng)建的ContextImpl   
    app.attach(context);
    return app;
    }

類加載進(jìn)來后直接反射創(chuàng)建實(shí)例,調(diào)用attach方法
frameworks/base/core/java/android/app/Application.java

final void attach(Context context) {
     attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

該方法做了2件事
a:調(diào)用了ContextWrapper的方法,該方法即將傳入的context(此處為上面創(chuàng)建的ContextImpl)賦值給mBase成員變量,此處即為Application的mBase
b:將AtivityThead中的packageInfo,即剛創(chuàng)建 ContextImpl賦值給自身成員變量的mPackageInfo,再賦值給Application的成員變量mLoadedApk。故此3個(gè)類的LoadApk為同一對象。

另外由以上的分析可知系統(tǒng)產(chǎn)生出來的PathClassLoader 僅在
1:packageInfo握有其成員變量引用,
2:當(dāng)前線程的classLoader
故要替換系統(tǒng)的類加載器只需要從這2個(gè)方面入手即可

總結(jié)

到此統(tǒng)ClassLoader相關(guān)及Application初始化相關(guān)跟蹤及總結(jié)結(jié)束,該文章為接下來跟蹤replugin-host-library的hook點(diǎn)的準(zhǔn)備知識(shí)

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

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

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