网上关于 “Android 开发" 的文章很多,我本人学习 Android 开发的过程也借鉴了网上先辈们的文章;但大多数文章都从底层的细枝末节开始讲述,由下而上给人一种这门技术“博大精深”望而生畏的感觉;而我写这篇文章的初衷就是由上而下,希望别人在阅读的过程中能够觉得 “模块化、组件化、插件化、热修复原来是这样的啊!” 的感觉
模块:
特点:
组件:
特点:
application组件: 是指该组件本身就可以运行并打包成apk
lib组件: 是指该组件属于app的一部分,可以供其它组件使用但是本身不能打包成apk
正常一个App中可以有多个module(模块),但是一般只会有一个module是设置为application的,其他均设置为library; 组件化开发就是要每个module都可以运行起来,因此在开发期间每个module均设置为application,发布时再进行合并。
Android项目中代码量达到一定程度,编译将是一件非常痛苦的事情; 短则一两分钟,长则达到五六分钟;随着app业务的壮大,模块越来越多,代码量超10万是很正常的
这个时候我们会遇到以下问题:
组件化开发可以有效降低代码模块的耦合度,使代码架构更加清晰,同时模块化的编译可以有效减少编译时间,当然总的编译时间是不会减少的,只是App模块化之后开发某个模块时,只需要编译特定模块,可以快速编译调试
基础类库主要是将各个组件中都会用到的一些基础库统一进行封装,例如网络请求、图片缓存、sqlite操作、数据加密等基础类库
这样可以避免各个组件都在自己的组件中单独引用,而且引用的版本可能都不一样,导致整个工程外部库混乱,统一了基础类库后,基础类库保持相对的稳定,这样各个组件对外部库的使用是相对可控的,防止出现一些外部库引出的极端问题,而且这样的话对于库的升级也比较好管理
对于每个组件都有一些是公共的抽象,例如我们工程中自己定义的BaseActivity、BaseFragment、自定义控件等,这些对于每个组件都是一样的,每个组件都基于一样的基础工程开发,一方面可以减少开发工作,另一方面也可以让各个组件的开发人员能够统一架构框架
这样每个组件的技术代码框架看起来都是一样的,也便于后期代码维护和人员互备
应用的主要业务逻辑在此实现,上面的几部分都实现以后,剩余的主要体力工作就是实现各个拆分出来的业务模块
壳工程主要用于将各个组件组合起来和做一些工程初始化,初始化包含了后续各个组件会用到的一些库的初始化,也包括ApplicationContext的初始化工作
在 Android 系统中,应用是以 Apk 的形式存在的,应用都需要安装才能使用; 但实际上 Android 系统安装应用的方式相当简单,其实就是把应用 Apk 拷贝到系统不同的目录下、然后把 so 解压出来而已
常见的应用安装目录有:
Apk 的构成,一个常见的 Apk 会包含如下几个部分:
其实 Android 系统在打开应用之后,也只是开辟进程,然后使用 ClassLoader 加载 classes.dex 至进程中,执行对应的组件而已
那大家可能会想一个问题,既然 Android 本身也是使用类似反射的形式加载代码执行,凭什么我们不能执行一个 Apk 中的代码呢?
这其实就是插件化的目的: 让 Apk 中的代码(主要是指 Android 组件)能够免安装运行,这样能够带来很多收益,最显而易见的优势其实就是通过网络热更新、热修复
说起热修复,已经是目前Android开发必备技能
我所了解的一种实现方式就是类加载方案,即 dex 插桩; 这种思路在插件化中也会用到;除此之外,还有底层替换方案,即修改替换 ArtMethod;采用类加载方案的主要是以腾讯系为主,包括微信的 Tinker、饿了么的 Amigo;采用底层替换方案主要是阿里系的 AndFix 等热修复的应用场景
热修复就是在APP上线以后,如果突然发现有缺陷了,如果重新走发布流程可能时间比较长,重新安装APP用户体验也不会太好; 热修复就是通过发布一个插件,使APP运行的时候加载插件里面的代码,从而解决缺陷,并且对于用户来说是无感的(用户也可能需要重启一下APP)
Java负责加载class文件的就是类加载器(ClassLoader); APP启动的时候,会创建一个自己的ClassLoader实例,我们可以通过下面的代码拿到当前的ClassLoader
ClassLoader classLoader = getClassLoader();
Log.i(TAG, "[onCreate] classLoader" + ":" + classLoader.toString());
然后我们在看一下构造函数。在ClassLoader 这个类中的 loadClass() 方法,它调用的是另一个2个参数的重载 loadClass() 方法
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
我们点进去深入看一下loadClass这个方法:
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;
}
通过分析loadClass方法,你会发现,ClassLoader加载类的方法就是loadClass,是通过双亲委派模型(Parents Delegation Model)实现类的加载的; 既在加载一个字节码文件时,会询问当前的classLoader是否已经加载过此字节码文件
如果加载过,则直接返回,不再重复加载; 如果没有加载过,则会询问它的Parent是否已经加载过此字节码文件,同样的,如果已经加载过,就直接返回parent加载过的字节码文件,而如果整个继承线路上的classLoader都没有加载过,才由child类加载器(即,当前的子classLoader)执行类的加载工作
整个流程大致可以归纳成如下三步
1、加载流程
检查当前的 classLoader 是否已经加载琮这个 class ,有则直接返回,没有则进行第2步。 调用父 classLoader 的 loadClass() 方法,检查父 classLoader 是否有加载过这个 class ,有则直 接返回,没有就继续检查上上个父 classLoader ,直到顶层 classLoader 。 如果所有的父 classLoader 都没有加载过这个 class ,则最终由当前 classLoader 调用 findClass() 方法,去dex文件中找出并加载这个 class
2、优点
而采用这种类的加载机制的优点就是如果一个类被classLoader继承线路上的任意一个加载器加载过,后续在整个系统的生命周期中,这个类都不会再被加载,大大提高了类的加载效率
类加载的共享功能: 一些Framework层级的类一旦被顶层,classLoader加载过,会缓存到内存中,以后在任何地方用到,都不会去重新加载,大大提高了效率;
类加载的隔离功能: 不同继承线路上的 classLoader 加载的类,肯定不是同一个类,这样可以避免某些开发者自己去写一 些代码冒充核心类库,来访问核心类库中可见的成员变量。如 java.lang.String 在应用程序启动前就 已经被系统加载好了,如果在一个应用中能够简单的用自定义的String类把系统中的String类替换掉 的话,会有严重的安全问题
基本上关于Android开发的模块化、组件化、插件化、热修复的简述就到这里完结了,希望认真看完的朋友能有所收获!如有不正之处还望指正!
需要本文中底层源码的同学
私信发送“源码”或“笔记”即可 获取 完整代码 以及 更多Android学习笔记+源码解析+面试视频
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
| 留言与评论(共有 0 条评论) “” |