【Android 插件化】插件化原理 ( 类加载器 )

简介: 【Android 插件化】插件化原理 ( 类加载器 )

一、" 插件化 " 中的 dex 文件


现在的大型 Android 项目 , 基本都是 组件化 + 插件化 开发 , 项目架构上都是 组件化 的框架 , 某些修改频繁的 Module 模块 , 设置成 " 插件 " 模块 , 编译成独立的 APK 文件 , 以 " 插件 " 的形式进行部署 , 供 " 宿主 " 模块调用 ;


应用运行时 , 点击启动某个 " 插件 " APK 中的界面 , 首先先 下载对应的 插件 APK 文件 , 将其放在 内置存储区 中 , 然后加载该 APK 文件 , 主要是 类加载器 DEX 文件中的 Class 字节码数据 ;


9.png



在上述项目中 , app 模块是 " 宿主 " 模块 , plugin 模块是 " 插件 " 模块 , 二者都是 " Phone & Tablet Module " 类型的应用 ,

2021053120021734.png



plugin 插件模块 , 编译出的 APK 文件如下 , 其 Java 类都封装在 " classes.dex " 文件中 , 在其中可以找到 PluginActivity.class 的字节码文件 ; 找到该模块后 , 可以将其加载到应用中 , 并跳转到该界面中 ;


1.png


二、类加载器分析


类加载 是 通过类加载引擎 , 将字节码数据加载到 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 {

}



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;
    }
}




目录
相关文章
|
2月前
|
Android开发 移动开发 小程序
binder机制原理面试,安卓app开发教程
binder机制原理面试,安卓app开发教程
binder机制原理面试,安卓app开发教程
|
2月前
|
Android开发
Android 插件化
Android 插件化
24 0
|
2月前
|
存储 Java Android开发
Android系统 设置第三方应用为默认Launcher实现和原理分析
Android系统 设置第三方应用为默认Launcher实现和原理分析
186 0
|
7天前
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
16 2
|
2月前
|
Android开发 异构计算 前端开发
Android显示原理,安卓自定义view面试
Android显示原理,安卓自定义view面试
|
2月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
2月前
|
存储 Android开发
Android插件化-Broadcast篇,2024年最新安卓面试自我介绍
Android插件化-Broadcast篇,2024年最新安卓面试自我介绍
|
2月前
|
Android开发 Kotlin API
Android插件化探索与发现,kotlin协程切换线程
Android插件化探索与发现,kotlin协程切换线程
|
2月前
|
缓存 Android开发
Android插件化——高手必备的Hook技术,零基础开发android
Android插件化——高手必备的Hook技术,零基础开发android
|
2月前
|
网络协议 Shell Android开发
Android 深入学习ADB调试原理(1)
Android 深入学习ADB调试原理(1)
76 1