ART – 双亲委派机制
motivation
了解热修复实现机制,能够hook 热修复中的代码。
热修复:
不通过重新下载安装包,安装apk ,来更新APK的目的。
ClassLoader
用于装载Class的装载器,这个类加载器能达到什么效果?
- 用户无需关心类加载的流程。
- 实现用时加载。
一些基础的ClassLoader:
BootClassLoader 加载系统基础库,比如 java.lang.String 。
PathClassLoader 用于加载Apk中的dex文件,整个apk的class都可以找到。
DexClassLoader 常用于插件化设计,插件的ClassLoader集成该类。
加载逻辑
- 如果一个类加载器加载了一个类,那么这个类加载器将这个类缓存起来。每次加载类的时候,首先会先访问指定类加载器的缓存,若缓存中存在,就直接从缓冲区里返回即可,这可以提高类加载的性能。(未从代码发现该逻辑)
- 如果缓冲区找不到,说明是第一次加载,那么如果指定类加载器有父加载器,则优先让父加载器加载该类。所以类加载器具有层级关系。值得注意的是,负责加载 Java 基本类和扩展类的类加载器必须处于类加载器层级中的顶层。父加载器加载类的过程也遵循同样的规则。
- 如果没有父加载器或者父加载器也加载不到,则返回当前类加载器,由自己读入字节码并委托虚拟机执行后续加载过程且返回生成的类。
相关代码实现
public abstract class ClassLoader {
...
...
...
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
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
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
...
...
...
}
插件化实例
MyDexClassLoader的声明,关注类的继承关系. MyDexClassLoader <- DexClassLoader <- BaseDexClassLoader <- ClassLoader
package com.catherine.classloader;
import android.content.Context;
import android.util.Log;
import java.net.URL;
import java.util.Enumeration;
import dalvik.system.DexClassLoader;
/**
* Created by Catherine on 2017/2/13.
* yacatherine19@gmail.com
*/
/**
* DexClassLoader:
* A class loader that loads classes from .jar and .apk files containing a classes.dex entry.
* This can be used to execute code not installed as part of an application.
*/
public class MyDexClassLoader extends DexClassLoader {
private static final String TAG = "MyDexClassLoader";
/**
* @param application MyApplication
* @param dexPath String: the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android
* @param soPath native libraries (path of .so files)
* @param classloader the parent class loader
*/
public MyDexClassLoader(MyApplication application, String dexPath, String soPath, ClassLoader classloader) {
super(dexPath, application.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath(), soPath.replace("files", "lib"),
classloader);
Log.i(TAG, "DexClassLoader " + classloader.toString());
Log.i(TAG, "dexPath " + dexPath);
Log.i(TAG, "optimizedDirectory " + application.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath());
Log.i(TAG, "soPath_lib " + soPath);
Log.i(TAG, "librarySearchPath " + soPath.replace("files", "lib"));
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Log.i(TAG, "findClass:" + name);
return super.findClass(name);
}
@Override
public String findLibrary(String name) {
Log.i(TAG, "findLibrary:" + name);
return super.findLibrary(name);
}
@Override
protected URL findResource(String name) {
Log.i(TAG, "findResource:" + name);
return super.findResource(name);
}
@Override
protected Enumeration<URL> findResources(String name) {
Log.i(TAG, "findResources:" + name);
return super.findResources(name);
}
@Override
protected synchronized Package getPackage(String name) {
Log.i(TAG, "getPackage:" + name);
return super.getPackage(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException{
Log.d(TAG, "loadClass:" + name);
return super.loadClass(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
Log.d(TAG, "loadClass:" + name + " : " + resolve);
return super.loadClass(name, resolve);
}
}
看看DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code DexClassLoader} that finds interpreted and native
* code. Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
*
* <p>The path lists are separated using the character specified by the
* {@code path.separator} system property, which defaults to {@code :}.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param optimizedDirectory this parameter is deprecated and has no effect
* @param librarySearchPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
MyDexClassLoader加载APK:
Object layerP = new MyDexClassLoader(this, dexPath, cc.getFilesDir().getAbsolutePath(),
(ClassLoader) sm.get());
使用loadClass加载MyDexClassLoader的class ,
getClassLoader().loadClass(MyConfig.APK2_ACTIVITY_MAIN);
真实调用到ClassLoader的loadClass
一个问题
在使用MyDexClassLoader 前:
使用MyDexClassLoader后:
莫非这个getClassLoader返回的ClassLoader 会变化吗?
getClassLoader 官网介绍返回类的加载器,类没变,类加载器没变,但是类加载的继承关系变了。。。有点意思 。
https://developer.android.com/reference/java/lang/Class#getClassLoader()
巧的是,刚好出现了两个继承关系。
这里用getClassLoader.loadClass(“com.catherine.resource1.MainActivity”)或者
getClassLoader.getParent().loadClass(“com.catherine.resource1.MainActivity”)都成功load到了目标类。
且都调用到MyDexClassLoader的loadClasss方法, 符合双亲委派模型,
另一个问题
class 加载流程呢?
明确继承关系 MyDexClassLoader.getParent == BootClassLoader
在demo中MyDexClassLoader中调用getParent方法直接返回了BootClassLoader,
因为用继承关系时给的参数就是BootClassLoader。
参考链接得知, 双亲委派关系在加载类时(loadClass) , 先检测是否已经加载过了,没有加载过, 再交给父类加载? ,但是这里是bootClassLoader能是由它来加载吗,它加载的话,不就所有的ClassLoader都可以用了吗?怀疑可能还是有路径的限制。
这还有一篇比较好的文章讲这个双亲委托机制,但是也没有回答android 双亲委派的加载流程。
可能得再看下。
再来一个问题
用默认ClassLoader加载java.lang.String 按照我们的理解,
但是实际上我们用loadClass,
直接就进行了返回,甚至没有走到MyDexClassLoader,????
只能说算是初步了解双亲委派模型, 后续熟练android底层调试后,再开展更细节的研究。
怀疑缓存
0 条评论