Android Binder机制彻底梳理一
Binder架构图:
先来瞅一下它的整体架构图:

其中粉红部分是上层的Binder,而蓝色的则是下层的Binder,很显然上层的是依赖于下层的。
什么是Binder【有个大概了解】?
这里从几个层面来说一下:
从IPC角度来说:
定义:Binder是Android中的一种跨进程通信方式,该通信方式在linux中没有,是Android独有的。
作用:在Android中实现跨进程通信。
从Android Driver层来说:
其实也就是指这一层:

定义:Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder。
备注:驱动层位于Linux内核中,它提供了最底层的数据传递,对象标识,线程管理,调用过程控制等功能。驱动层是整个Binder机制的核心。
从Android Native层来说:
这一层:

定义:Binder是创建Service Manager以及BpBinder/BBinder模型,搭建与binder驱动的桥梁。
从Android Framework层来说:
定义:Binder是各种Manager(ActivityManager、WindowManager等)和相应xxxManagerService的桥梁。
从Android APP层来说:
定义:Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的 Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
Binder核心源码分析【通过底层C来纵览】:
我们平常不管在是学习还在是面试时聊天Android的跨进程技术都会说到Binder,那它到底长啥样呢?我们知道它底层是用C来实现的,所以接下来从内核层的角度去认识一下它,当然里面的细节可能看不懂,但有个大概印象既可,这样会加深对Android的Binder跨进程机制有一个更加深刻的理解,而非只停留在理论层面。那既然要分析内核层的代码,得先找到相关代码嘛,这里采用之前【https://www.cnblogs.com/webor2006/p/10708754.html】分析Android的热修复原理时说到的一个方式来查看:http://androidxref.com/,如下:

Binder驱动:
首先来瞅一下Binder驱动,也就是这块东东:

其中图中的这个东东到底是对应哪段代码:

还有这个东东确实是存在么?

或者说上图中画的到底准不准确?所以一切答案都得从源码中来寻找答案,下面就开始找寻答案,对于Binder来说,它其实在底层是对应这个代码:binder.c,咱们在线找一下它:


所以不可能全部都分析到,主要分析一下主流程,下面开始。
binder_init():驱动设备初始化。
先来找到这个函数:


具体里面注册了啥这里就不深究了,重在了解注流程。
binder_open():打开Binder驱动设备。
初始化了之后,接下来则会打开Binder驱动设备,简单分析一下对应代码:

binder_mmap():申请内存。
它的作用先贴一下,有个了解,反正细节也不太懂:“首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。”
看一下该函数:

binder_ioctl():数据操作。
之前提出的问题的答案就出现了,回忆下:

它确实在底层对应的代码,图没有画错,看下代码:

然后它里面有很多操作,大概瞅一下:

好,大致过一下,晕晕的,反正大概知道在底层确实是有一个Binder驱动这个东东。
ServiceManager:
先看咱们的架构图:

对于服务的注册和服务的获取我们都需要通过ServiceManager,那下面看下它的一些主流程,当然它也有对应的c代码啦,它的代码是在Android系统的Native层了,而非Android的内核层,所以这里以9.0系统为例搜一下它:



下面则来分析一下主流程:
启动ServiceManager:
先来看一下这个启动流程的时序图,有个全局观:

下面先来看一下它的启动入口在哪?

好,接下来依据流程图来分析一下:
- binder_open()打开binder驱动。

对应代码:

而此方法其实调用的是binder.c的,所以其做的事就是时序图中画的2、3、4,如下:
这个在分析binder驱动源码时都有看到过相关的函数,下面再简单介绍一下:

- binder_become_context_manager() 注册成为binder服务的大管家。
先看一下时序图对应的流程:
对应的代码处:

而它的实现是这样的:

也就是是通过ioctl() 让其成为上下文的管理者,而该函数最终会经过系统调用调用Binder驱动层的binder_ioctl()方法,整个系统中只有一个这样的管理者。
- binder_loop() 进入无限循环,处理client端发来的请求。
对应时序流程的这块:
找下代码:


最后则会调用这个函数中的do_add_service()和do_find_service(),如下:

获取ServiceManager:
也来看一下获取的时序图:

下面再来看个描述:
获取Service Manager是通过defaultServiceManager()函数来完成,当进程注册服务(addService)或获取服务(getService)的过程之前,都需要先调用defaultServiceManager()方法来获取gDefaultServiceManager对象。对于gDefaultServiceManager对象,如果存在则直接返回;如果不存在则创建该对象,创建过程包括调用open()打开binder驱动设备,利用mmap()映射内核的地址空间。
根据时序图画的,先找到IServiceManager,里面有个defaultServiceManager()方法,下面简单分析一下:

用于获取BpServiceManager对象。

它是用于获取ProcessState对象(也是单例模式),每个进程有且只有一个ProcessState对象,存在则直接返回,不存在则创建。


它用于获取BpBinder对象,对于handle=0的BpBinder对象,存在则直接返回,不存在才创建。其实也就是对应这块的东东:

do_add_service()注册服务:
接下来再来看一下注册服务的大体流程:

然后咱们也来看一张时序图,以注册MediaPlayerService为例,如下:

按着时序图来大致分析一下:

所以瞅一下这个添加服务的方法:

然后:

也就是:

继续照着流程分析:


而它里面的实现主要是:


这里就不再继续往下分析了,反正最终最终会调用这来:

里面大致分析一下:
svc_can_register() 检查权限,检查selinux权限是否满足。
find_svc() 服务检索,根据服务名来查询匹配的服务。

svcinfo_death() 释放服务,当查询到已存在同名的服务,则先清理该服务信息,再将当前的服务加入到服务列表svclist。

do_find_service()查询服务:
主要是看一下这块:


find_svc() 从svclist服务列表中,根据服务名遍历查找是否已经注册。当服务已存在svclist,则返回相应的服务名,否则返回NULL。
framework层分析:
这里先来看一个Binder相关架构的核心类图:

接下来咱们得往Frameworkd层进行分析了,如下:

初始化:
在Android系统开机过程中,Zygote启动时会有一个虚拟机注册过程,该过程调用AndroidRuntime::startReg方法来完成jni方法的注册,所以咱们从这块进行分析:

找到对应的代码:

然后看一下startReg()方法:

而其中这个数组中就有注册Binder的函数,如下:

而该函数的定义是在这里:

对应代码为:

下面简单分析一下这三个注册函数:
int_register_android_os_Binder():注册Binder,建立了Binder类在Native层与framework层之间的相互调用的桥梁。

int_register_android_os_BinderInternal():注册BindrInternal,建立了BinderInternal类在Native层与framework层之间的相互调用的桥梁。

int_register_android_os_BinderProxy():注册BinderProxy,建立了BinderProxy类在Native层与framework层之间的相互调用的桥梁。

注册服务:
此时就会涉及到Java代码的分析了,主要是这块:

其添加的入口是在:

而它最终调用的是它:



也就是:

获取服务:
它的入口也是在ServiceManager当中,如下:


其中最终会调用ServiceManagerProxy代理类的getService(),如下:

手写Binder通信--AIDL:
经过了漫长枯燥的源码分析已经对Binder机制有了进一步了解之后,下面咱们来撸撸码来写一个AIDL,然后写完之后再以这个例子再来对它进行一个原理剖析,这样对于Binder的机制应该了解得就非常之透了,由于这块的写法比较简单就不多说,重点是通过这个例子好分析整个它的背后机制。好,下面开始撸码,既然是C/S结构,则先新建两个app,一个代表c端,一个代表s端:


编写服务端:
在服务端中新建一个AIDL文件,如下:

这里以一个添加人员的非基础类型为例,基础类型的比较简单这里就不以它为例了,先新建一个Person类:

它里面定义为:
public class Person implements Parcelable {
private String name;
private int grade;
public Person(String name, int grade) {
this.grade = grade;
this.name = name;
}
protected Person(Parcel in) {
this.grade = in.readInt();
this.name = in.readString();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int i) {
dest.writeInt(this.grade);
dest.writeString(name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
然后再定义一个这个类的AIDL文件:

然后再来定义咱们的IMyAidl:

好,接下来则定义一个服务:


这样系统就会扫码已经定义在清单中的所有服务然后将其注册到我们的ServiceManager当中,也就是:

最后咱们在应用启动时来将此服务启动一下,如下:

然后咱们就运行将它启动一下:

接下来编写咱们的客户端代码了。
编写客户端:
首先将服务端的AIDL定义的文件拷进来,注意要连包名目录是一模一样的,如下:

然后它里面用到了一个实体类Person,也需要原包名进行复制,如下:

然后咱们再来启动一下服务端的那个服务,其代码如下:
public class MainActivity extends AppCompatActivity {
private IMyAidl myAidl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
private void bindService() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.binderserver", "com.android.binderserver.MyBinderService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myAidl = IMyAidl.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
然后咱们增加几个点击按钮,来看一下整个的效果:

处理下点击事件:

运行看一下效果:

以上就是一个AIDL的完整过程,也就是Android跨进程通讯的典型案例,由于篇幅原因关于它的整个流程梳理放到下次。
Android Binder机制彻底梳理一的更多相关文章
- Android Binder机制彻底梳理二
根据AIDL了解整体调用流程[重点分析AIDL流程]: 在上一次https://www.cnblogs.com/webor2006/p/11741743.html中我们已经对Android Binde ...
- 【转】Android - Binder机制
以下几篇文章是分析binder机制里讲得还算清楚的 目录 1. Android - Binder机制 - ServiceManager 2. Android - Binder机制 - 普通servic ...
- 浅谈android binder机制
binder机制 是谷歌优化在android上更适合终端的IPC(多进程通信方式),满足系统对通信方式,传输性能和安全性的要求. 特性: 1. 用驱动程序来推进进程间的通信.2. 通过共享内存来提高性 ...
- Android Binder机制简单了解
Binder -- 一种进程间通信(IPC)机制, 基于OpenBinder来实现 毫无疑问, 老罗的文章是不得不看的 Android进程间通信(IPC)机制Binder简要介绍和学习计划 浅谈Ser ...
- android binder机制之——(创建binder服务)
Binder机制编程 前面的几篇文章具体介绍了android中binder机制的方方面面,相信你对binder机制已经有了较深刻的理解.俗话说得好"学以致用",以下我们就通过在 ...
- Android Binder机制详解:手写IPC通信
想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...
- Android binder机制---概述
1.进程间通讯的原因 目前操作系统都使用虚拟存储技术,管理内存. 假设是32位机器,0-3G是用户空间,3-4G是系统使用.虚拟内存和逻辑内存都按4K分页.这样虚拟内存和逻辑内存就存在对应关系. 一个 ...
- android binder机制详解
摘要 Binder是android中一个很重要且很复杂的概念,它在系统的整体运作中发挥着极其重要的作用,不过本文并不打算从深层次分析Binder机制,有两点原因:1是目前网上已经有2篇很好的文章了,2 ...
- ANDROID BINDER机制浅析
Binder是Android上一种IPC机制,重要且较难理解.由于Linux上标准IPC在灵活和可靠性存在一定不足,Google基于OpenBinder的设计和构想实现了Binder. 本文只简单介绍 ...
随机推荐
- 使用ReadtheDocs托管技术文档
ReadtheDocs Read the Docs非常适合写软件文档以及编写一些教程.电子书之类.对于一些一两篇文章就能写清楚的可以记笔记或写博客, 但是如果要写成一个系列的,不如写成一本书的形式,更 ...
- [转] 浅谈 OpenResty
一.前言 我们都知道Nginx有很多的特性和好处,但是在Nginx上开发成了一个难题,Nginx模块需要用C开发,而且必须符合一系列复杂的规则,最重要的用C开发模块必须要熟悉Nginx的源代码,使得开 ...
- linux 高级
linux命令: top 查看整机的性能: ----(看内存(mem)和cpu) 1:查看cpu的cpu的核数按1连续: 2:id=idle(空闲率),值越大越好, 3:load av ...
- ElasticSearch中"distinct","count"和"group by"的实现
最近在业务中需要使用ES来进行数据查询,在某些场景下需要对数据进行去重,以及去重后的统计.为了方便大家理解,特意从SQL角度,方便大家能够理解ES查询语句. 1 - distinct ; { &quo ...
- SET key value [EX seconds] [PX milliseconds] [NX|XX]
SET key value [EX seconds] [PX milliseconds] [NX|XX] 可用版本: >= 1.0.0 时间复杂度: O(1) 将字符串值 value 关联到 k ...
- python罗列oss文件
# 列举文件 import oss2 from itertools import islice AccessKeyId = "your accesskeyid" AccessKey ...
- java之spring之整合ssh
这篇主要讲解spring + struts2 + hibernate : 目录结构如下: t_role t_user 1.新建 web项目 :spring_ssh 2.在 WebRoot/WEB-IN ...
- 用lua求两个数组的交集、并集和补集。
-- 克隆 function Clone(object) local lookup_table = { } local function _copy(object) if type(object) ~ ...
- C# vb .net实现真实阴影特效滤镜
在.net中,如何简单快捷地实现Photoshop滤镜组中的真实阴影效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第 ...
- Golang中的RegExp正则表达式用法指南
------------------------------------------------------------ Golang中的正则表达式 ------------------------- ...