Android类加载机制学习
来源:互联网 发布:bugs软件 编辑:程序博客网 时间:2024/06/07 01:03
Android类加载机制学习
1、Android类加载机制
Android系统的Dalvik虚拟机如同其他Java虚拟机一样,在运行程序时首先需要将对应的类加载到内存中。
在Java标准的虚拟机中,类加载可以从.class文件中读取,也可以是其他形式的二进制流,因此,Java程序开发常常利用这一点,在程序运行时手动加载Class,从而达到代码动态加载执行的目的。
然而Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类加载机制上,他们是不同的。例如,Android的类加载不能直接从.class文件中读取,而是从.dex文件中读取;此外,在使用标准Java虚拟机时,我们经常自定义继承自ClassLoader的类加载器。然后通过defineClass()方法来从一个二进制流中加载Class。然而,这在Android里是行不通的,参看源码我们知道,Android中ClassLoader的defineClass()方法除了抛出一个“UnsupportedOperationException”之外,什么都没做。
• Dalvik虚拟机类加载机制
在Dalvik虚拟机里,defineClass()方法被弃用,我们如何实现动态加载类呢?
Android为我们从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader,并且在ClassLoader中定义了loadClass()方法。
DexClassLoader和PathClassLoader这两个继承自BaseDexClassLoader,BaseDexClassLoader继承自ClassLoader,BaseDexClassLoader本质上是覆写了ClassLoader的findClass()方法。Android中,ClassLoader用loadClass()方法来加载我们需要的类,我们可以参看ClassLoader部分源码:
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { //检查本ClassLoader是否已经加载 Class<?> clazz = findLoadedClass(className); //如果本ClassLoader没有加载 if (clazz == null) { ClassNotFoundException suppressed = null; //从父ClassLoader中查找 try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { suppressed = e; } if (clazz == null) { //调用findClass来加载 try { clazz = findClass(className); } catch (ClassNotFoundException e) { e.addSuppressed(suppressed); throw e; } } } return clazz;}
DexClassLoader和PathClassLoader都属于符合双亲委派模型的类加载器(因为它们没有重载loadClass()方法)。
其特点如下:
会先查询当前ClassLoader实例是否加载过此类,有就返回
如果没有,查询Parent是否已经加载过此类,如果已经加载过,就直接返回Parent加载的类
如果parent路线上的ClassLoader都没有加载,才调用findClass执行类的加载工作
ClassLoader与PathClassLoader和DexClassLoader的类图关系如下图所示:
• BaseDexClassLoader的findClass加载类过程
findClass加载类过程示意图如下:
findClass方法类加载的过程如下:
1、生成Element数组dexElements,生成pathList
BaseDexClassLoader的构造方法如下:
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);}
BaseDexClassLoader中的构造方法包含4个参数,他们分别是:
(1) dexPath : 指目标类所在的APK或jar文件的路径.类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,分隔符为File.pathSeparator(“:”).
(2) optimizedDirectory : 由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是指定解压出的dex文件存放的路径.
(3) libraryPath : 指目标类中所使用的C/C++库存放的路径
(4) parent : 是指该装载器的父装载器
在BaseDexClassLoader中生成DexPathList类的对象pathList,在DexPathList的构造方法中调用DexPathList类中的makePathElements()方法,在makePathElements()方法中会调用splitDexPath()方法,如果dexPath包含多个路径,splitDexPath()方法能够提取出File.pathSeparator分隔的多个路径,并将多个路径放入List中返回,然后makePathElements()方法会调用loadDexFile()方法遍历List中每个包含dex的文件生成DexFile,用生成的DexFile生成Element对象,用这些Element对象构成Element数组dexElements。
DexPathList.java中的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); }}
一个应用程序中所有类加载到内存中,就是在makePathElements执行loadDexFile()方法来实现的。
optimizedDirectory是一个内部存储路径,用来缓存我们需要加载的dex文件的。DexClassLoader可以指定自己的optimizedDirectory,所以它可以加载外部的dex,因为这个dex会被复制到内部路径的optimizedDirectory;而PathClassLoader没有optimizedDirectory,所以它只能加载内部的dex,这些大都是存在系统中已经安装过的apk里面的。
它们的不同之处总结如下是:
DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk;
PathClassLoader只能加载系统中已经安装过的apk,Android中大部分类的加载默认采用此类
2、pathList调用BaseDexClassLoader中的findClass()方法
调用BaseDexClassLoader的findClass()方法,在此方法,pathList调用DexPathList中的findClass()方法,BaseDexClassLoader的findClass()方法如下:
@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException { 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;}
3、调用DexPathList中的findClass()方法
pathList是DexPathList类型的一个对象,DexPathList中的findClass()方法源码如下:
public Class findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null;}
DexPathList的findClass()方法,遍历dexElements元素的DexFile实例,也就是遍历所有加载过的dex文件,一个个调用loadClassBinaryName()方法,看能不能找到我们想要的类,如果可以,返回这个类。
DexFile.java中的loadClassBinaryName()方法:
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, suppressed);}private static Class defineClass(String name, ClassLoader loader, Object cookie,List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie); } catch (NoClassDefFoundError e) { if (suppressed != null) { suppressed.add(e); } } catch (ClassNotFoundException e) { if (suppressed != null) { suppressed.add(e); } } return result;}private static native Class defineClassNative(String name, ClassLoader loader, Object cookie) throws ClassNotFoundException, NoClassDefFoundError;
4、返回
将加载的类返回ClassLoader类的loadClass()方法。
总结
当加载一个类的时候,需要首先获取类加载器,调用类加载器的loadClass方法,在loadClass方法中先查询当前ClassLoader实例是否加载过此类,有就返回;如果没有,查询parent指定的类加载器是否已经加载过此类,如果已经加载过,就直接返回parent加载的类;如果parent路线上的ClassLoader都没有加载,才调用findClass执行类的加载工作。BaseDexClassLoader的findClass方法调用之前,首先在BaseDexClassLoader的构造方法中生成了DexPathList类的对象pathList,在DexPathList类的构造方法中,利用loadDexFile()方法将dex文件中的类加载到内存中,并生成对应的DexFile对象,进而生成Element对象,用这些Element对象构成Element数组dexElements。在BaseDexClassLoader的findClass方法中,调用了DexPathList类的findClass方法,在DexPathList类的findClass方法里,遍历dexElements元素的DexFile实例,一个个调用loadClassBinaryName()方法,看能不能找到我们想要的类,如果可以,返回这个类。
2、类加载器的生成
在应用程序第一次启动时,都会首先创建Application对象。会调用ActivityThread.java中的handleBindApplication()方法,handleBindApplication()方法调用getPackageInfoNoCheck()方法生成LoadedApk对象data.info,它们的源代码如下:
ActivityThread.java中的handleBindApplication()方法:
private void handleBindApplication(AppBindData data) { LoadedApk data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ...... //调用LoadedApk.java中makeApplication()方法 Application app = data.info.makeApplication(data.restrictedBackupMode, null); ...... }
ActivityThread.java中getPackageInfoNoCheck()方法:
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true, false);}
getPackageInfoNoCheck()方法调用了getPackageInfo()方法,getPackageInfo()方法的源代码如下:
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,ClassLoader baseLoader, boolean securityViolation, boolean includeCode,boolean registerPackage) { final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (differentUser) { // Caching not supported across users ref = null; } else if (includeCode) { // ref = mPackages.get(aInfo.packageName); } else { ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref != null ? ref.get() : null; if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null ? mBoundApplication.processName : null) + ")"); //生成LoadedApk类的对象packageInfo packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); if (mSystemThread && "android".equals(aInfo.packageName)) { packageInfo.installSystemApplicationInfo(aInfo, getSystemContext().mPackageInfo.getClassLoader()); } if (differentUser) { // Caching not supported across users } else if (includeCode) { //将生成的packageInfo存入到WeakReference<LoadedApk> mPackages中 mPackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } } return packageInfo; }}
第一次调用getPackageInfo()方法,WeakReference mPackages为null,利用new LoadedApk()生成LoadedApk类的对象packageInfo,并将生成的packageInfo存入到WeakReference mPackages中。当第二次调用getPackageInfo()方法时,WeakReference mPackages不为null,直接返回WeakReference mPackages中存储的LoadedApk对象。可以看到同一个应用程序中,类加载所使用的LoadedApk对象都是同一个。
生成LoadedApk对象data.info后,在ActivityThread.java中的handleBindApplication()方法中调用了makeApplication()方法生成Application类的对象,makeApplication()方法在LoadedApk.java文件中定义的,其源码为:
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { try { java.lang.ClassLoader cl = getClassLoader(); ...... //调用ContextImpl类的createAppContext()方法 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } return app;}
Instrumentation.java中newApplication()方法:
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return newApplication(cl.loadClass(className), context);}static public Application newApplication(Class<?> clazz, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { Application app = (Application)clazz.newInstance(); app.attach(context); return app;}
可见,调用newApplication()方法之前,第一次调用了LoadedApk的getClassLoader()方法,其源码如下:
public ClassLoader getClassLoader() { synchronized (this) { if (mClassLoader != null) { return mClassLoader; } if (mIncludeCode && !mPackageName.equals("android")) { ...... //调用了ApplicationLoaders.java里面的getClassLoader()方法 mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); } else { if (mBaseClassLoader == null) { mClassLoader = ClassLoader.getSystemClassLoader(); } else { mClassLoader = mBaseClassLoader; } } return mClassLoader; }}
当第一次调用LoadedApk的getClassLoader()方法,mClassLoader==null,会调用ApplicationLoaders.java里面的getClassLoader()方法生成mClassLoader;当再次调用LoadedApk的getClassLoader()方法,mClassLoader!=null,则将第一次生成的mClassLoader直接返回。可见,getClassLoader()返回的ClassLoader也是同一个。
调用了ApplicationLoaders.java里面的getClassLoader()方法,源码如下:
public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent){ ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent(); synchronized (mLoaders) { if (parent == null) { parent = baseParent; } if (parent == baseParent) { ClassLoader loader = mLoaders.get(zip); if (loader != null) { return loader; } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); PathClassLoader pathClassloader = new PathClassLoader(zip, libPath, parent); 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; }}
在这个方法中调用了生成了PathClassLoader对象,根据前面的讲解,可以知道在PathClassLoader的构造方法中,调用了makePathElements()方法,完成了所有的类加载到内存中。这个PathClassLoader就是我们应用程序中类加载所用的类加载器。
总结
类加载器生成的流程图:
在应用程序启动时,会创建Application对象,在创建Application的过程中,完成了应用程序中所有类的加载到内存。当再使用这个程序中其他类的时候,再次调用ActivityThread.java中的getPackageInfo()方法,此方法不会再new一个新的LoadedApk对象,而是返回创建Application时的那个LoadedApk对象,即同一个LoadedApk对象,用这个对象调用getClassLoader获得的加载器也都是同一个,不会重复加载类,生成类的实例的形式一般为:
getClassLoader().loadClass(className).newInstance();
Android常用的类包括Activity、Service、BroadcastReceiver、Fragment,都采用了同样的方式用同一个类加载器生成类的实例。
比如:
① 使用Fragment时,在Fragment.java文件的instantiate()方法中调用了loadClass()方法。
/** * Create a new instance of a Fragment with the given class name. This is * the same as calling its empty constructor. */public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { try { Class<?> clazz = sClassMap.get(fname); if (clazz == null) { // Class not found in the cache, see if it's real, and try to add it clazz = context.getClassLoader().loadClass(fname); if (!Fragment.class.isAssignableFrom(clazz)) { throw new InstantiationException("Trying to instantiate a class " + fname + " that is not a Fragment", new ClassCastException()); } sClassMap.put(fname, clazz); } //生成Fragment类的单例 Fragment f = (Fragment)clazz.newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = args; } return f; } catch (ClassNotFoundException e) { } catch (java.lang.InstantiationException e) { } catch (IllegalAccessException e) { }}
② 在使用BroadcastReceiver时,在ActivityThread.java的handleReceiver()方法中调用了loadClass()方法。
private void handleReceiver(ReceiverData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); String component = data.intent.getComponent().getClassName(); LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); IActivityManager mgr = ActivityManagerNative.getDefault(); BroadcastReceiver receiver; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); receiver = (BroadcastReceiver)cl.loadClass(component).newInstance(); } catch (Exception e) { ...... } ......}
③ 在使用Service时,在ActivityThread.java的handleCreateService()方法中调用了loadClass()方法。
private void handleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = (Service) cl.loadClass(data.info.name).newInstance(); } catch (Exception e) { ...... } ......}
④ 在Activity的使用过程中,在Instrumentation.java的newActivity()方法中调用了loadClass()方法。
public Activity newActivity(ClassLoader cl, String className, Intent intent)throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity)cl.loadClass(className).newInstance();}
这些类调用getClassLoader方法所获得的类加载器是同一个,用loadClass获取到对应的Class,采用newInstance()生成实例。
- Android类加载机制学习
- Android Class类加载机制
- jvm学习笔记(6)类加载机制
- Java虚拟机学习 - 类加载机制
- Java虚拟机学习 - 类加载机制
- 学习 Java 的类加载机制
- Java虚拟机学习 - 类加载机制
- Java虚拟机学习 - 类加载机制
- Java虚拟机学习 - 类加载机制
- JVM学习03-类加载机制
- Java学习笔记-虚拟机类加载机制
- Java虚拟机学习 - 类加载机制
- Java学习笔记--类加载机制
- Java虚拟机学习 - 类加载机制
- Java虚拟机学习 - 类加载机制
- Java虚拟机学习 - 类加载机制
- JVM类加载机制学习记录
- 类的加载机制学习笔记
- MySQL运行原理与基础架构
- 欢迎使用CSDN-markdown编辑器
- 【windows内核编程】vs2013+WDK8.1+winDbg+vmware win7虚拟机联调
- oracle 查看调度作业和job的表
- Eclipse编译AIDL报错couldn't find import for class
- Android类加载机制学习
- 由HITCON 2016一道web聊一聊php反序列化漏洞
- Android Studio常用快捷键(提取全局变量等)
- Windows(gitlab为例)系统下git常用操作
- yii2 实现简单的留言板组件
- java--过滤器
- Binary Tree Paths ---LeetCode
- 区块链开源项目Ripple四、共识(1)
- python第一课笔记