在Android中要学习Binder那肯定就绕不开IPC(进程间通信机制)。进程间通信,顾名思义就是将数据从某一进程传递到另一进程的过程。这就涉及到Linux进程中内存空间的划分了。如图所示:

对Binder通信机制的理解

传统IPC,相当于生活中,我们将快递从北京寄到上海。此时,北京的用户需要将快递交到快递员手中,对于快递员而言就是copy_from_user(),然后快递员再通过快递公司的运输将快递交到上海的用户(接收方)手中,对于上海的快递员而言就是copy_to_user()。这个过程在程序中相当于将数据copy了两次。而Binder通信机制相当于我们发送的快递接收方就是上海的快递员,这样就省去了在上海快递员将快递交到用户(接收方)手中的过程,省去了copy_to_user。这相比传统的IPC更快一些。Binder通过mmap映射机制将数据映射到共享的内核空间从而实现减少一次数据copy的目的。下面盗用一张表格来比较说明一下Binder与传统IPC的优缺点。

Binder

共享内存

Socket

性能

需要拷贝一次

无需拷贝

需要拷贝两次

特点

基于C/S 架构

易用性高

控制复杂,易用性差

基于C/S 架构

作为一款通用接口,其传输效率低,开销大

安全性

为每个APP分配UID

同时支持实名和匿名

依赖上层协议

访问接入点是开放的

不安全

依赖上层协议

访问接入点是开放的

不安全

安全性:共享内存和Socket依赖上层协议,它们是不安全的,Binder是比较安全的。

理解:共享内存和Socket相当于将要传输的数据包括PID和UID打成一个数据包传输到系统中,而此时系统拿到的PID是APP传递过来的不是系统本身通过自己的机制得到的,系统是没有辨别真伪的能力的。这就相当于拿着伪造的身份证到社会中做事情,我们知道社会中的广大民众一般是无法识别身份证的真伪的,这样会造成很大的安全隐患。而Binder机制中,系统会为每个APP分配PID和UID(这也就相当于公安局会为每个公民设定并发放一张身份证一样),这就好比拿着假的身份证去公安局作案,这不得被当场抓获吗。所以Binder机制的安全性就得到了保证。

Android-Binder运行机制总结

Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种进程间通信的机制。从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应ManagerService的桥梁;从Android应用程序来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。在Android开发中,Binder主要用于Service中。

首先我们先看一下Binder运行机制图,图是我自己画的有些丑,大家不要见怪

上面提到,Binder是一种进程间的通信机制,Android中我们经常会使用AIDL来完成跨进程的通信。现在我们就来了解一下AIDL的调用过程。首先我们思考三个问题:

1. 客户端如何获取到AIDL的遥控器的?

2. 通过这个遥控器是如何调用到服务端的?

3. 服务端是如何处理的?

带着这三个问题我们去分析AIDL的工作流程。

1.在Android-studio中新建一个Book类,并实现android自己的序列化接口Parcelable,并完成其步骤


public class Book implements Parcelable {public int bookId;public String bookName;public Book(int bookId, String bookName) {this.bookId = bookId;this.bookName = bookName;}protected Book(Parcel in) {bookId = in.readInt();bookName = in.readString();}public static final Creator<Book> CREATOR = new Creator<Book>() {@Overridepublic Book createFromParcel(Parcel in) {return new Book(in);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(bookId);dest.writeString(bookName);}
}

2. 新建一个Book.aidl文件

只需要

parcelable Book;

这表示Book.aidl是Book类在AIDL中的声明。

3. 新建一个IBookManager.aidl文件

// 引用一定要写,即使是在同一包下
import com.binderstudydemo.Book;
// Declare any non-default types here with import statementsinterface IBookManager {//用于从远程服务端获取图书列表List<Book> getBookList();//用于往图书列表中添加一本书void addBook(in Book book);
}

4. 系统会为我们生成一个IBookManager.java 类,其代码如下

/** This file is auto-generated.  DO NOT MODIFY.* Original file: C:\\AndroidSources\\androidStudyDemo\\BinderStudyDemo\\app\\src\\main\\aidl\\com\\binderstudydemo\\IBookManager.aidl*/
package com.binderstudydemo;
// Declare any non-default types here with import statementspublic interface IBookManager extends android.os.IInterface {/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.binderstudydemo.IBookManager {//Binder的唯一标识private static final java.lang.String DESCRIPTOR = "com.binderstudydemo.IBookManager";/*** Construct the stub at attach it to the interface.*/public Stub() {// 注释1this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.binderstudydemo.IBookManager interface,* generating a proxy if needed.*//*** 用于将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象,这种转换过程是区分进程的,* 如果客户端和服务端在同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy*/public static com.binderstudydemo.IBookManager asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}// 注释2android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.binderstudydemo.IBookManager))) {return ((com.binderstudydemo.IBookManager) iin);}return new com.binderstudydemo.IBookManager.Stub.Proxy(obj);}/*** 此方法用于返回当前Binder对象* @return*/@Overridepublic android.os.IBinder asBinder() {return this;}/*** 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理*/@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_getBookList: {data.enforceInterface(DESCRIPTOR);java.util.List<com.binderstudydemo.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBook: {data.enforceInterface(DESCRIPTOR);com.binderstudydemo.Book _arg0;if ((0 != data.readInt())) {_arg0 = com.binderstudydemo.Book.CREATOR.createFromParcel(data);} else {_arg0 = null;}this.addBook(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.binderstudydemo.IBookManager {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.binderstudydemo.Book> getBookList() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.binderstudydemo.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.binderstudydemo.Book.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}@Overridepublic void addBook(com.binderstudydemo.Book book) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain(); // 3android.os.Parcel _reply = android.os.Parcel.obtain(); // 4try {_data.writeInterfaceToken(DESCRIPTOR);if ((book != null)) {_data.writeInt(1);book.writeToParcel(_data, 0);} else {_data.writeInt(0);}// 注释5mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}public java.util.List<com.binderstudydemo.Book> getBookList() throws android.os.RemoteException;public void addBook(com.binderstudydemo.Book book) throws android.os.RemoteException;
}

代码有些长,但这是系统自动生成的,我们想着分析其中的处理流程不得不把代码都贴出来。

首先我们对这个类做个分析,IBookManager.Java这个类,它继承了 IInterface 这个接口,同时它自己也还是个接口,所有可以在Binder中传输的接口都需要继承 IInterface 接口。

现在我们来回到上面提出的三个问题。对于问题1

在客户端连接到服务端后(bindService)后,回调方法onServiceConnected中有一句这样的代码:

iAidl = IBookManager.Stub.asInterface(service);

在Stub的asInterface方法中注释2处,调用了Binder的queryLocalInterface(DESCRIPTOR)方法,我们可以跟进代码,Binder的queryLocalInterface方法代码如下:

 /*** Use information supplied to attachInterface() to return the* associated IInterface if it matches the requested* descriptor.*/
public IInterface queryLocalInterface(String descriptor) {if (mDescriptor.equals(descriptor)) {return mOwner;}return null;
}// 而成员变量mDescriptor的赋值是在Binder的attachInterface方法中,代码如下/*** Convenience method for associating a specific interface with the Binder.* After calling, queryLocalInterface() will be implemented for you* to return the given owner IInterface when the corresponding* descriptor is requested.*/
public void attachInterface(IInterface owner, String descriptor) {mOwner = owner;mDescriptor = descriptor;
}

注意:这个attachInterface方法在Stub的构造方法注释1中调用了,这也就是说明了DESCRIPTOR是在Stub的构造方法中赋值的。客户端的调用并没有哦对Stub做初始化操作,所以为空;而在服务端中继承自Service重写的onBind方法返回的IBinder对象,它的初始化时new了一个AIDL.Stub();所以服务端中的DESCRIPTOR不为空,所以客户端调用Stub.asInterface方法时里面的queryLocalInterface(DESCRIPTOR)返回值为空,因此该方法返回的是Proxy。调用queryLocalInterface方法的目的是当Activity和Service在同一进程时我们就不需要再去通过Binder来传递信息了。对于非同一进程我们拿到的是Proxy对象的引用,此时我们就可以调用Proxy的方法了,Proxy实现了我们在自定义AIDL中的方法getBookList和addBook方法。此时就可以通过Proxy进行写入操作了,_data包用来存储发送到服务端的数据,_reply包用来存储服务端返回来的数据。问题1的解释就到此结束了。

现在我们继续分析问题2

在客户端我们通过AIDL类型IBookManager的实例对象调用到了addBook方法(此处我们只拿一个方法举例)。代码就走到了IBookManager的最后一行,我们继续看重写该方法的地方,此时代码就走到了Proxy的addBook方法了。此处做了_data 包的写入工作,注释5处,调用了Binder的transact方法,代码如下:

public final boolean transact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {if (false) Log.v("Binder", "Transact: " + code + " to " + this);if (data != null) {data.setDataPosition(0);}// 调用了onTransact方法,boolean r = onTransact(code, data, reply, flags);if (reply != null) {reply.setDataPosition(0);}return r;}

该方法中,调用了onTransact方法,IBookManager的Stub类中重写了onTransact方法。onTransact方法中也有对我们自定义的AIDL中的两个方法的调用。至此,问题2我们也说明白了。

注意:在Proxy中mRemote.transact(Stub.TRANSACTION_addBook,_data,_reply,0);调用该方法进入Binder后,会将客户端的线程挂起来,直到服务端有数据返回后,客户端的线程才会继续运行。

此函数参数一的含义:为了让客户端告诉服务端调用的是哪个方法,它是一个int型的值(在Proxy的最后进行的赋值),在经过AIDL后服务端可以知道该int型的值对应的方法。

参数三:flags,0代表可发送可返回;1代表不可返回。

现在看最后一个问题3

由上可知Binder的onTransact调用到了Stub的onTransact方法,服务端在该方法接收数据并作出处理。通过参数code判断调用的是哪个方法。还接着上面的流程,分析调用了this.addBook(....)方法,但是IBookManager的内部类Stub并没有实现addBook,所以此处的addBook调用的正是真正的服务Service中的addBook方法。问题3的分析到此结束了。至此,整个从客户端到服务端的调用以及服务端作出处理的过程我们都分析完了。

至于说Activity中调用IBookManager.addBook,绑定Service的过程,以及Service创建并返回IBinder的过程我没有给出代码,这个就是一个很平常的使用。本文的目的就是分析AIDL的过程。

最后附上AIDL的时序图:

关于Binder的基础学习,之前我已经写过一篇了,本文是在原有基础上增加了AIDL调用过程的详细分析。在文章一开头出现的那张Linux内存空间划分的图我是盗用的其他人的。希望勿怪。

在此我要感谢享学课堂的Leo老师,将AIDL的调用流程讲解的很详细。再次表示感谢。

Android-Binder及AIDL基础分析相关推荐

  1. 【Android】Android Binder进程间通信AIDL示例与源码分析

    前言 众所周知,Android进程间通信采用的是Binder机制.Binder是Android系统独有的进程间通信方式,它是采用mmp函数将进程的用户空间与内核空间的一块内存区域进行映射,免去了一次数 ...

  2. aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉

    AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言.由于 Android 系统的 ...

  3. Android Service和Binder、AIDL

    为什么80%的码农都做不了架构师?>>>    Android Service和Binder.AIDL 人收藏此文章, 关注此文章发表于3个月前 , 已有 206次阅读 共 个评论  ...

  4. Android—Binder+AIDL

    Binder Binder机制优点: 只需要进行一次数据拷贝,性能上仅次于共享内存. 基于C/S架构,职责明确,架构清晰,稳定性较好. 安全性好,为每个App分配UID,UID是鉴别进程身份的标志. ...

  5. 由浅入深 学习 Android Binder(一)- AIDL

    Android Binder系列文章: 由浅入深 学习 Android Binder(一)- AIDL 由浅入深 学习 Android Binder(二)- bindService流程 由浅入深 学习 ...

  6. Android Binder机制浅析及AIDL的使用

    参考 轻松理解 Android Binder,只需要读这一篇 图文详解 Android Binder跨进程通信的原理 Android中的Parcel是什么 Android Binder IPC通信机制 ...

  7. 不得不说的Android Binder机制与AIDL

    说起Android的进程间通信,想必大家都会不约而同的想起Android中的Binder机制.而提起Binder,想必也有不少同学会想起初学Android时被Binder和AIDL支配的恐惧感.但是作 ...

  8. Android:学习AIDL,这一篇文章就够了(下)

    前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识.强烈建议 ...

  9. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  10. 插件化知识储备-Binder和AIDL原理

    前言 插件化技术火热已久,为什么会有插件化,时势造英雄吧,随着移动互联网的快速发展,业务的飞速增长,如何在有限时间给用户提供高质量的APP,当线上出现各种BUG,如何快速修复并发布上线,插件化的意义也 ...

最新文章

  1. Python中strip()、lstrip()、rstrip()用法详解
  2. 【机器视觉】 continue算子
  3. SAP Spartacus payment detail page的CMS模型
  4. Oracle 索引概述
  5. PLSQL大数据生成规则
  6. git:Failed to connect to github.com port 443:.....
  7. dataTransfer.getData()在dragover,dragenter,dragleave中无法获取数据的问题
  8. ora01017 linux,Oracle linux 7.5安装oracle 12c dg一直提示ORA-01017
  9. 交叉路口红绿灯控制程序linux,西门子PLC编程实例详解|十字路口交通灯自动控制系统...
  10. Python 万能代码模版:数据可视化篇
  11. LeetCode 247. 中心对称数
  12. 极课大数据完成C轮融资,投资方为好未来
  13. Java随笔记 - TCP通信的基本过程,三次握手,四次挥手
  14. 解决无法下载/502 com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+
  15. cpy几天爬出密道问题
  16. Virtualbox安装Kylin 10后调整屏幕分辨率无法选中保存按钮
  17. Mysql语句计算文本字数_使用SQL来确定文本字段的字数统计
  18. 客户体验和客户服务的区别
  19. epic games 无法 下载 unreal engine5
  20. React.createElement的理解使用

热门文章

  1. Excel动态图表制作
  2. Totoro中文分词第二版上线啦
  3. 2018年会季攻略之会议礼品如何选择?
  4. 中北信商2019年计算机考试题,中北信商高數习题答案.doc
  5. 华为电脑Linux怎么恢复出厂设置,华为MateBook笔记本怎么恢复出厂设置还原系统?详细教程...
  6. 谷歌全新 App 广告线上课程,让您的 App 推广事半功倍
  7. android 4g 内存,追了苹果8年,内存才4G,安卓12G内存你羡慕吗?
  8. 如何把PDF转换成图片格式
  9. Kubernetes入门指南-基础篇
  10. 硬盘数据突然消失怎么回事?硬盘数据突然消失怎么找回