文章目录
-
- 系列前言
- 一. Binder机制
-
- 为什么需求Binder
- Binder模型
- Binder与插件化
- 总结
- 二. ClassLoader
-
- 双亲托付模型
- 总结
- 参考资料
系列前言
Hello,夸姣的一周又开端啦,让咱们开端愉快的学习吧。
从今天开端,我会花较多的时刻来跟咱们一起学习Android插件化。这一篇文章是Android插件化的发动篇。
Android插件化是之前几年里的一个很火的技能概念。从2012年开端就有人在研讨这门技能。从粗糙的AndroidDynamicLoader框架,到第一代的DroidPlugin等,继而发展到第二代的VirtualApk,Replugin等,再到现如今的VirtualApp,Atlas。插件化在国内逐渐的发展和完善,却也在近几年呈现了RN等替代品以后慢慢会走向弱势。
尽管插件化技能的研讨热潮现已曩昔,可是这门技能本身仍是有着很多的技能实践,关于咱们了解Android机制很有协助。所以从这篇文章开端我会写一系列的文章,加上自己对插件化的实践,最后会去从源码视点分析几个优秀的插件化库,构成一套完好的插件化的理论体系。
下面是插件化的技能框架,也是我这个系列文章的行文思路
一. Binder机制
网上分析Binder机制的文章现已很多了,在这篇文章里,我不会去解说Binder的运用,而是会去解说清楚Binder的规划思路,规划原理和关于插件化的运用。
为什么需求Binder
首要咱们知道,Android是基于Linux内核开发的。关于Linux来说,操作体系为一个二进制可履行文件创建了一个载有该文件自己的栈,堆、数据映射以及同享库的内存片段,还为其分配特其他内部管理结构。这便是一个进程。操作体系有必要供给公正的运用机制,使得每个进程能正常的开端,履行和终结。
这样呢,就引入了一个问题。一个进程能不能去操作其他进程的数据呢?咱们能够想一下,这是肯定不能呈现的,尤其是体系级的进程,假如被其他进程影响了可能会形成整个体系的坍塌。所以咱们很自然的想到,咱们应该把进程隔离起来,linux也是这样做的,它的虚拟内存机制为每个进程分配连续的内存空间,进程只能操作自己的虚拟内存空间。一起,还有必要满意进程之间保持通讯的才能,究竟团结力量大,单凭单个进程的独立运作是不能撑起操作体系的功用需求的。
为了解决这个问题,Linux引进了用户空间User Space和内核空间Kernel Space的区别。用户空间要想访问内核空间,唯一办法便是体系调用。内核空间经过接口把应用程序请求传给内核处理后回来给应用程序。一起,用户空间进程假如想升级为内核空间进程,需求进行安全检查。
补充知识:体系调用首要经过两个办法:
copy_from_user():将用户空间的数据拷贝到内核空间
copy_to_user():将内核空间的数据拷贝到用户空间
以上便是linux体系的跨进程通讯机制。而咱们立刻要说的Binder,便是跨进程的一种办法
Binder模型
Binder是一种进程间通讯(IPC)办法,Android常见的进程中通讯办法有文件同享,Bundle,AIDL,Messenger,ContentProvider,Socket。其间AIDL,Messenger,ContentProvider都是基于Binder。Linux体系经过Binder驱动来管理Binder机制。
Binder的完成基于mmap()体系调用,只用拷贝一次,比常规文件页操作更快。微信开源的MMKV等也是基于此。有爱好的能够了解一下。
首要Binder机制有四个参加者,Server,Client两个进程,ServiceManager,Binder驱动(内核空间)。其间ServiceManager和Binder驱动都是体系完成的,而Server和Client是需求开发者自己完成的。四者之中只要Binder驱动是运转在内核空间的。
这儿的ServiceManager作为Manager,承当着Binder通讯的树立,Binder的注册和传递的才能。Service担任创建Binder,并为他起一个字符形式的名字,然后把Binder和名字经过**经过Binder驱动,借助于ServiceManager自带的Binder向ServiceManager注册。**注意这儿,因为Service和ServiceManager也是跨进程通讯需求Binder,ServerManager是自带Binder的,所以相对ServiceManager来说Service也就相当于Client了。
Service注册了这个Binder以后,Client就能经过名字取得Binder的引证了。这儿的跨进程通讯两边就变成了Client和ServiceManager,然后ServiceManager从Binder表取出Binder的引证返给Client,这样的话假如有多个Client的话,屡次回来引证就行了,可是事实上引证的都是放在ServiceManager中的Service。
当Client经过Binder驱动跟Service通讯的时候,往往需求获取到Service的某个方针object。这时候为了安全考虑,Binder会把object的署理方针proxyobject回来,这个方针具有一模一样的办法,可是没有具体才能,只担任接纳参数传给真正的object运用。
所以完好的Binder通讯进程是
OK,跨进程通讯就讲清楚了。接下来咱们讲讲插件化中的Binder。
Binder与插件化
首要,咱们先回忆一下Activity的发动进程,Instrumentation调用了ActivityManagerNative,这个AMN是咱们的本地方针,然后AMN调用getDefault拿到了ActivityManagerProxy,这个人AMP便是AMS在本地的署理。相当于binder模型中的Client,而这个AMP承继的是IActivityManager,具有四大组件的所有需求AMS参加的办法。本地经过调用这个AMP办法来间接地调用AMS。这样,咱们就调用到了AMS发动了Activity。
那么,AMS怎么与Client进行通讯呢?现在咱们经过Launcher发动了Activity,肯定要告诉Launcher “没你什么事了,你洗洗睡吧”。咱们能够看到,这儿两边的人物就发生了改变,AMS需求去发消息,承当Client的人物,而Launcher这时候作为Service供给服务。而这次通讯相同也是运用的Binder机制。AMS这边保存了一个ApplicationThreadProxy方针,这个方针便是Launcher的ApplicationThread的署理。AMS经过ATP给App发消息,App经过ApplicationThread处理。
以上,便是Binder机制在Android中的运用,咱们后边会经过hook这个进程完成插件化。
总结
看了这么多,可能仍是很多朋友不明白Binder。我也是这样,很长一段时刻都不知道Binder到底指的是啥。后来我看到了这样一种界说:
- 从进程间通讯的视点看,Binder 是一种进程间通讯的机制;
- 从 Server 进程的视点看,Binder 指的是 Server 中的 Binder 实体方针;
- 从 Client 进程的视点看,Binder 指的是对 Binder 署理方针,是 Binder 实体方针的一个长途署理
- 从传输进程的视点看,Binder 是一个能够跨进程传输的方针;Binder 驱动会对这个跨过进程鸿沟的方针对一点点特别处理,自动完成署理方针和本地方针之间的转化。
二. ClassLoader
双亲托付模型
Java中默许有三种ClassLoader。分别是:
- BootStrap ClassLoader:发动类加载器,最顶层的加载器。首要担任加载JDK中的核心类。在JVM发动后也随着发动,并结构Ext ClassLoader和App ClassLoader。
- Extension ClassLoader:扩展类加载器,担任加载Java的扩展类库。
- App ClassLoader:体系类加载器,担任加载应用程序的所有jar和class文件。
- 自界说ClassLoader:需求承继自ClassLoader类。
ClassLoader默许运用双亲托付模型来搜索类。每个ClassLoader都有一个父类的引证。当ClassLoader需求加载某个类时,先判别是否加载过,假如加载过就回来Class方针。不然交给他的父类去加载,持续判别是否加载过。这样 层层判别,就到了最顶层的BootStrap ClassLoader来企图加载。假如连最顶层的Bootstrap ClassLoader都没加载过,那就加载。假如加载失败,就转交给子ClassLoader,层层加载,直到最底层。假如还不能加载的话那就只能抛出异常了。
经过这种双亲托付模型,好处是:
- 更高效,父类加载一次就能够避免了子类屡次重复加载
- 更安全,避免了外界伪造java核心类。
Android中的ClassLoader
android从5.0开端运用art虚拟机,这种虚拟机在程序运转时也需求ClassLoader将类加载到内存中,可是与java不同的是,java虚拟机经过读取class字节码来加载,可是art则是经过dex字节码来加载。这是一种优化,能够兼并多个class文件为一个classes.dex文件。
android一共有三种类加载器:
- BootClassLoader:父类结构器
- PathClassLoader:一般是加载指定路径/data/app中的apk,也便是装置到手机中的apk。所以一般作为默许的加载器。
- DexClassLoader:从包括classes.dex的jar或许apk中,加载类的加载器,可用于动态加载。
看PathClassLoader和DexClassLoader源码,都是承继自BaseDexClassLoader。
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent); //见下文
}
}
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent); //见下文
//搜集dex文件和Native动态库
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
咱们能够看到PathClassLoader的两个参数都为null,标明只能承受固定的dex文件,而这个文件是只能在装置后呈现的。而DexClassLoader中optimizedDirectory,和librarySearchPath都是能够自己界说的,说明咱们能够传入一个jar或许apk包,确保解压缩后是一个dex文件就能够操作了。因而,咱们一般运用DexClassLoader来进行插件化和热修正。
能够看到,BaseDexClassLoader有一个相当重要的进程便是初始化DexPathList。初始化DexPathList的进程首要是搜集dexElements和nativeLibraryPathElements。一个Classloader能够包括多个dex文件,每个dex文件被封装到一个Element方针。这element方针在初始化和热修正逻辑中是相当重要的。当查找某个类时,会遍历dexElements,假如找到就回来,不然持续遍历。所以当多个dex中有相同的类,只会加载前面的dex中的类。下面是这段逻辑的具体完成
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
//找到方针类,则直接回来
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
return null;
}
总结
咱们先是解说了Java中类加载的双亲托付机制,然后介绍了Android中的几种ClassLoader,从源码视点介绍了两种ClassLoader加载机制的不同。以后的插件化实践中,咱们会经常用到DexClassLoader。
参考资料
Android跨进程通讯:图文详解 Binder机制 原理
写给 Android 应用工程师的 Binder 原理剖析
深入了解Android中的ClassLoader
Android类加载器ClassLoader