概述
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

2:Android中的ClassLoader:

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,從緩存中獲取名為packageInfo的LoadedApk,沒有則創(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è)方法即LoadedApk的makeApplication.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可能為ContextImpl或ApplicationContext,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í)