APP的功能模块越来越多,体积越来越大,这样可以将一些业务模块做成插件化,按需加载,从而减小安装包的体积
模块之间的耦合度高,协同开发沟通成本越来越大
方法数目可能超过65535,APP占用的内存过大
应用之间的互相调用
组件化与插件化的区别
组件化开发就是将一个app分成多个模块,每个模块都是一个组件,开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一成一个apk,这就是组件化开发。
插件化开发和组件化略有不同,插件化开发是将整个app拆分成多个模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk,最终打包的时候宿主apk和插件apk分开打包。
各插件化框架对比
市面上比较流行的插件化框架也有很多,他们之间都有哪些区别呢?
插件化实现
插件apk是没有安装的,那么怎么让宿主去加载它呢?我们知道,一个apk是有代码和资源组成的,所以只需要考虑两个问题即可:
如何加载插件中的类?
如何加载插件中的资源?
当然还有最重要的一个问题,四大组件如何调用呢?四大组件是需要注册的,而插件apk中的组件显然不会在宿主提前注册,那么如何去调用它呢?
下面我们就来一步一步解决这些问题
ClassLoader类加载器
以前在讲热修复的时候,我简单地介绍了一下ClassLoader的加载机制。java源码文件在编译后会生成一个class文件,而在Android中,将代码编译后会生成一个 apk 文件,将 apk 文件解压后就可以看到其中有一个或多个 classes.dex 文件,它就是安卓把所有 class 文件进行合并,优化后生成的。
java 中 JVM 加载的是 class 文件,而安卓中 DVM 和 ART 加载的是 dex 文件,虽然二者都是用的 ClassLoader 加
载的,但因为加载的文件类型不同,还是有些区别的,所以接下来我们主要介绍安卓的 ClassLoader 是如何加载
dex 文件的。
ClassLoader实现类
在Android中,ClassLoader是一个抽象类,它的实现类主要分为两种类型:系统类加载器(BootClassLoader),和自定义类加载器(PathClassLoader | DexClassLoader)
先看一下ClassLoader加载流程图:
BootClassLoader
用于加载Android Framework层的class文件,比如 Activity、Fragment,不过需要注意的是AppCompatActivity虽然也是google工程师提供的类,但是一个第三方包中的类,并不输入Framwork层,所以AppCompatActivity并不是使用BootClassLoader加载的
PathClassLoader
用于Android应用程序类加载器。可以加载指定的dex, 以及jar、zip、apk中的classes.dex
DexClassLoader
在Android8.0以后的API中,和 PathClassLoader是没有任何区别的,而在以前的API中,两者只有一个设置加载路径的区别(有的文章说,PathClassLoader只支持直接操作dex格式文件,而DexClassLoader可以支持.apk、.jar和.dex文件,并且会在指定的outpath路径释放出dex文件。其实不然,甚至可以说两者没有任何区别)
先放一张ClassLoader类继承关系图,相信都能看懂,就不多讲了,下面来看一下PathClassLoader 和 DexClassLoader的源码:
// /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
// optimizedDirectory 直接为 null
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
// optimizedDirectory 直接为 null
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
// API 小于等于 26/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
// 26开始,super里面改变了,看下面两个构造方法
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
// API 26/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
// DexPathList 的第四个参数是 optimizedDirectory,可以看到这儿为 null
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
}
// API 25/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
}
根据源码就可以了解到,PathClassLoader 和 DexClassLoader 都是继承自 BaseDexClassLoader,且类中只有构造方法,它们的类加载逻辑完全写在 BaseDexClassLoader 中。
其中我们值的注意的是,在8.0之前,它们二者的唯一区别是第二个参数 optimizedDirectory,这个参数的意思是
生成的 odex(优化的dex)存放的路径,PathClassLoader 直接为null,而 DexClassLoader 是使用用户传进来的
路径,而在8.0之后,二者就完全一样了。
下面我们再来了解下 BootClassLoader 和 PathClassLoader 之间的关系:// 在 onCreate 中执行下面代码
ClassLoader classLoader = getClassLoader();
while (classLoader != null) {
Log.e(“leo”, “classLoader:” + classLoader);
classLoader = classLoader.getParent();
}
Log.e(“leo”, “classLoader:” + Activity.class.getClassLoader());
打印结果:
classLoader:dalvik.system.PathClassLoader[DexPathList[[zip file
“/data/user/0/com.enjoy.pluginactivity/cache/plugin-debug.apk”, zip file
"/data/app/com.enjoy.pluginactivity-T4YwTh-
8gHWWDDS19IkHRg==/base.apk"],nativeLibraryDirectories=[/data/app/com.enjoy.pluginactivity-
T4YwTh-8gHWWDDS19IkHRg==/lib/x86_64, /system/lib64, /vendor/lib64]]]
classLoader:java.lang.BootClassLoader@a26e88d
classLoader:java.lang.BootClassLoader@a26e88d
通过打印结果可知,应用程序类是由 PathClassLoader 加载的,Activity 类是 BootClassLoader 加载的,并且
BootClassLoader 是 PathClassLoader 的 parent,这里要注意 parent 与父类的区别。这个打印结果我们下面还
会提到。
加载原理
那么如何使用类加载器去从dex中加载一个插件类呢?很简单
比如,有一个apk文件,路径是apkPath,里面有个类com.plugin.Test,就可以通过反射加载一个类:
// 初始化一个类加载器
DexClassLoader classLoader = new DexClassLoader(dexPath, context.getCacheDir().getAbsolutePath, null, context.getClassLoader);
// 获取插件中的类
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
结语
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。
再附一部分Android架构面试视频讲解:
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
)收录**
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算