【Android 插件化】插件化原理 ( 类加载器 )
Android 插件化系列文章目录
【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化】插件化原理 ( 类加载器 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
文章目录
一、" 插件化 " 中的 dex 文件
现在的大型 Android 项目 , 基本都是 组件化 + 插件化 开发 , 项目架构上都是 组件化 的框架 , 某些修改频繁的 Module 模块 , 设置成 " 插件 " 模块 , 编译成独立的 APK 文件 , 以 " 插件 " 的形式进行部署 , 供 " 宿主 " 模块调用 ;
应用运行时 , 点击启动某个 " 插件 " APK 中的界面 , 首先先 下载对应的 插件 APK 文件 , 将其放在 内置存储区 中 , 然后加载该 APK 文件 , 主要是 类加载器 DEX 文件中的 Class 字节码数据 ;
在上述项目中 , app 模块是 " 宿主 " 模块 , plugin 模块是 " 插件 " 模块 , 二者都是 " Phone & Tablet Module " 类型的应用 ,
plugin 插件模块 , 编译出的 APK 文件如下 , 其 Java 类都封装在 " classes.dex " 文件中 , 在其中可以找到 PluginActivity.class 的字节码文件 ; 找到该模块后 , 可以将其加载到应用中 , 并跳转到该界面中 ;
二、类加载器分析
类加载 是 通过类加载引擎 , 将字节码数据加载到 Java 虚拟机的运行期数据区 中的 Java 虚拟机栈 中 ;
ClassLoader 加载涉及到 双亲委派机制 , Android 中顶级的类加载器 ClassLoader 是 BootClassLoader , 然后其下是 PathClassLoader , PathClassLoader 与 DexClassLoader 基本相同 ;
DexClassLoader 继承了 BaseDexClassLoader , 没有实现任何逻辑 , 只是调用 BaseDexClassLoader 的构造方法进行初始化 ;
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
源码地址 : /libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
PathClassLoader 也是继承了 BaseDexClassLoader , 其中定义的构造方法与 DexClassLoader 相同 ;
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);
}
}
源码地址 : /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
因此 PathClassLoader 与 DexClassLoader 实现的功能基本相同 , 二者都可以用于加载 Dex 文件 ;
PathClassLoader 是 Google 官方使用的 , DexClassLoader 是提供给开发者使用的 , 使用类加载器时 , 尽量使用 DexClassLoader ;
三、获取类加载器
在 Activity 中调用如下方法 , 获取不同层级的 ClassLoader ;
getClassLoader()
getClassLoader().getParent()
getClassLoader().getParent().getParent()
代码示例 :
package kim.hsl.plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "getClassLoader() : " +
getClassLoader());
Log.i(TAG, "getClassLoader().getParent() : " +
getClassLoader().getParent());
Log.i(TAG, "getClassLoader().getParent().getParent() : " +
getClassLoader().getParent().getParent());
}
}
打印结果 :
I/MainActivity: getClassLoader() : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/kim.hsl.plugin-_uowA-lCCTOuGQrqkuXOeg==/base.apk"],nativeLibraryDirectories=[/data/app/kim.hsl.plugin-_uowA-lCCTOuGQrqkuXOeg==/lib/arm64, /system/lib64]]]
I/MainActivity: getClassLoader().getParent() : java.lang.BootClassLoader@fc7ec45
I/MainActivity: getClassLoader().getParent().getParent() : null
结果分析 :
在 Activity 中直接调用 getClassLoader()
方法获取的是 PathClassLoader ,
调用 getClassLoader().getParent()
方法获取的是 BootClassLoader ;
此外没有更高层级的 ClassLoader , getClassLoader().getParent().getParent()
方法获取的是空 ;
BaseDexClassLoader 的父类是 ClassLoader , ClassLoader 是个抽象类 , 从继承关系上 , 没有涉及到 BootClassLoader ;
在 ClassLoader 中 , 存在一个 ClassLoader parent
字段 , 该字段通过构造方法传入 , getClassLoader().getParent()
方法拿到的不是 ClassLoader , 而是指定的父类引用 ClassLoader parent
字段 ,
public class BaseDexClassLoader extends ClassLoader {
}
源码参考 : /libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public abstract class ClassLoader {
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
}
}
源码参考 : /libcore/ojluni/src/main/java/java/lang/ClassLoader.java
四、双亲委派机制
类加载器层级 : 由高到低 : BootClassLoader -> PathClassLoader / DexClassLoader ;
双亲委派机制 :
自定义的类加载器 MyClassLoader 加载一个 Class 类对象 Student , 可以 指定 parent 父类为 PathClassLoader , 该类会 向其上级父类 PathClassLoader 询问该 Student 类对象 是否被加载过 , 如果没有被加载过 ;
则继续向 上级父类 BootClassLoader 询问 Student 类对象 是否被加载过 , 如果被加载过 , 则返回类对象 , 如果没有被加载过 , 则开始委派子类进行加载 ;
BootClassLoader 委派子类 PathClassLoader 进行加载 Student 类对象 , PathClassLoader 就会委派 MyClassLoader 进行加载 , MyClassLoader 发现其没有子类 , 则开始进行类加载 Student 类对象 ;
在 ClassLoader 中的 loadClass 方法中 , 先调用了
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
方法 , 下检查该类是否被加载过 , 如果没有被加载过 , 则先判断父类是否为空 , 如果不为空 , 则调用父类的 loadClass 方法 ,
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
父类也调用父类的 loadClass 方法 , 如果调用到最顶层 , 没有父类 , 则开始加载 ;
ClassLoader 类加载相关源码 :
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;
}
}
源码参考 : /libcore/ojluni/src/main/java/java/lang/ClassLoader.java
相关文章
- android开机动画多长时间_Android开机动画原理分析
- Android浏览器多窗口webview界面截屏心得「建议收藏」
- 把Android系统签名弄成jks
- strictmode android,Android 应用性能优化-StrictMode(严格模式)
- android 模拟器 haxm,Android模拟器不使用HAXM
- android系统中toast是什么_Android个人资料简单布局
- android activitymanager 系统api_Android view
- android 复制控件,Android长按复制文本功能[通俗易懂]
- 学习笔记 | Android开发常用的几种模式
- app自动化测试(Android)-- 参数化用例
- 【Android 安全】DEX 加密 ( Application 替换 | 创建用户自定义 Application | 替换 ContextImpl 对象的 mOuterContext 成员 )
- 【Android 热修复】热修复原理 ( 热修复框架简介 | 将 Java 字节码文件打包到 Dex 文件 )
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )
- 【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
- 【Android Gradle 插件】AndroidSourceSets 配置 ① ( Overlay 重叠包机制 | 使用 sourceSets 配置多个 res 目录 )
- 【Android Gradle 插件】AndroidSourceSets 配置 ③ ( aidl 配置 | assets 配置 | compileConfigurationName 配置 )
- 【Android Gradle 插件】Android Module 模块 build.gradle 构建脚本 Groovy 语法分析 ① ( Gradle 二进制插件引入 | Gradle依赖配置 )
- 【Android Gradle 插件】Gradle 自定义 Plugin 插件 ③ ( 自定义插件作用 | Android Gradle 插件的扩展 | 自定义 Extension 扩展 )
- 【Android Gradle 插件】自定义 Gradle 插件模块 ④ ( META-INF 中声明自定义插件的核心类 | 在应用中依赖本地 Maven 仓库中的自定义 Gradle 插件 )
- [android] 新闻客户端主界面部分详解手机开发