@TryLoveCatch
2023-05-04T17:32:35.000000Z
字数 14462
阅读 1595
Android知识体系
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制
在android studio里面使用AIDL,我们直接在Server工程目录右键,新建AIDL文件,如下图:
修改IMyAidlInterface.aidl
interface IMyAidlInterface {
String getName();
}
这个就是我们这个Server提供的功能。
IMyAidlInterface.aidl
对应的java
文件,点击Make Project(下图中的小锤子) 在app->build->generated->source->aidl->debug
路径下面生成了IMyAidlInterface.java
。
public class MyService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// 检查权限
int tCheck = checkCallingOrSelfPermission("io.github.trylovecatch.AIDL_SERVICE");
if(tCheck == PackageManager.PERMISSION_DENIED){
return null;
}
return new MyBinder();
}
class MyBinder extends IMyAidlInterface.Stub
{
@Override
public String getName() throws RemoteException
{
return "哈哈哈";
}
}
}
Manifest
文件
<permission android:protectionLevel="normal" android:name="io.github.trylovecatch.AIDL_SERVICE"/>
注册MyService
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="io.github.trylovecatch.AIDL_SERVICE" />
</intent-filter>
</service>
好了,Server端到此,就全部结束了。
IMyAidlInterface.aidl
文件到Client工程,保证包名一直。Make Project
,同样会生成一个java文件。
Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");
tIntent.setPackage("io.github.trylovecatch.aidlserver");
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Toast.makeText(MainActivity.this, mAidlInterface.getName(), Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
Manifest
<uses-permission android:name="io.github.trylovecatch.AIDL_SERVICE"/>
整个过程就是这样,当点击Button
的时候,就会Toast
显示哈哈哈
,证明远程调用成功。
代码下载: 源代码
进程隔离
是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A
写入进程B
的情况发生。 进程的隔离实现,使用了虚拟地址
空间。进程A
的虚拟地址和进程B
的虚拟地址不同,这样就防止进程A
将数据信息写入进程B
。
操作系统的不同进程之间,数据不共享;对于每个进程来说,它都天真地以为自己独享了整个系统,完全不知道其他进程的存在;因此一个进程需要与另外一个进程通信,需要某种系统机制才能完成。
首先,我们说下Linux Kernel
:
它是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。
内核空间
Linux Kernel
的运行空间,就是Kernel space(内核空间)
。
Kernel space
可以执行任意命令,调用系统的一切资源。
用户空间
用户程序的运行空间,就是
User space(用户空间)
。
User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统调用(system call)
,才能向内核发出指令。
为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。这样就对Linux Kernel
起到了保护作用。
举个例子:
str = "my string" // 用户空间
x = x + 2
file.write(str) // 切换到内核空间
y = x + 4 // 切换回用户空间
第三行需要写入文件,就要切换到 Kernel space,因为用户不能直接写文件,必须通过内核安排
虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?
上面我们说过了
用户空间访问内核空间的唯一方式就是
系统调用(system call)
通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。用户软件良莠不齐,要是它们乱搞把系统玩坏了怎么办?因此对于某些特权操作必须交给安全可靠的内核来执行。
内核运行态(内核态)
,指一个任务(进程)执行系统调用而陷入内核代码中时。例如上面例子的第三行。
用户运行态(用户态)
,当进程在执行用户自己的代码时。例如上面例子的第一、二、四行。
通过系统调用
,用户空间
可以访问内核空间
,那么如果一个用户空间
想与另外一个用户空间
进行通信怎么办呢?
我们希望,最好操作系统内核能够支持,事实上,确实是支持的:传统的Linux
通信机制,比如Socket
,管道
等都是内核支持的。
但是,Binder
并不是Linux 内核
的一部分,它是怎么做到访问内核空间的呢?
答案就是:动态可加载内核模块(Loadable Kernel Module,LKM)
机制。
LKM
是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。
这样,Android
系统可以通过添加一个内核模块
运行在内核空间
,用户空间
之间可以通过这个模块作为桥梁,来完成通信了。
在Android
系统中,这个运行在内核空间
的,负责各个用户进程
通过Binder
通信的内核模块
叫做Binder驱动
;它是一种虚拟的物理设备,位于/dev/binder
路径下。
Linux
进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket
通信ip地址
是客户端手动填入的,都可以进行伪造;而Binder
机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性.通过上面的介绍,我们知道:
用户空间A
要和用户空间B
通信,必须借助内核空间
的支持,而Android
通过LKM
,添加了一个内核模块
到内核空间
,这个内核模块
,就是Binder驱动
。
我们举一个现实中的例子,来说明这个过程。
假设A
和B
要进行通信,通信的媒介是打电话(A
是Client
,B
是Server
)。A
要给B
通信:
B
把自己写入通讯录
里面A
通过通讯录
得到B
的电话A
拨打电话,通过基站
和B
建立链接A
和B
通信这里面有四个角色:
1. B
,Server
,提供服务,并把自己写入通讯录
里面
2. A
,Client
,发起通信的,需要B的服务
3. 通讯录
,保存了Server
的对应关系,叫做ServiceManager(SM)
4. 基站
,类似于Binder驱动
,通过它的帮助,才能彼此联系上
整个通信步骤如下:
SM
建立(建立通信录
);首先有一个进程向驱动提出申请为SM
;驱动
同意之后,SM
进程负责管理Service
。不过这时候通信录
还是空的,一个号码都没有。Server
向SM
注册(完善通信录
);每个Server
端进程启动之后,向SM
报告,我是zhangsan
,要找我请返回0x1234
(这个地址没有实际意义,类比);其他Server
进程依次如此;这样SM
就建立了一张表
,对应着各个Server
的名字
和地址
。Client
想要与Server
通信,首先询问SM
;请告诉我如何联系zhangsan
,SM
收到后给他一个号码0x1234
;Client
收到之后,开心滴用这个号码拨通了Server
的电话,于是就开始通信了。这里Client
与SM
的通信,以及Client
与Server
的通信,都会经过Binder驱动
,Binder驱动
是整个通信过程的核心。
上文给出了Binder
的通信模型,指出了通信过程的四个角色: Client
, Server
, SM
, driver
; 但是我们仍然不清楚Client
到底是如何与Server
完成通信的。
两个运行在用户空间的进程A和进程B如何完成通信呢?内核可以访问A和B的所有数据;所以,最简单的方式是通过内核做中转;假设进程A要给进程B发送数据,那么就先把A的数据copy到内核空间,然后把内核空间对应的数据copy到B就完成了;用户空间要操作内核空间,需要通过系统调用;刚好,这里就有两个系统调用:
copy_from_user
,copy_to_user
。
但是,Binder
机制并不是这么干的。讲这么一段,是说明进程间通信并不是什么神秘的东西。那么,Binder
机制是如何实现跨进程通信的呢?
首先,看一下系统根据我们的aidl
文件生成的java
文件,路径在app->build->generated->source->aidl->debug
:
package io.github.trylovecatch.aidlserver;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder
implements io.github.trylovecatch.aidlserver.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "io.github.trylovecatch.aidlserver.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an io.github.trylovecatch.aidlserver.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {
return ((io.github.trylovecatch.aidlserver.IMyAidlInterface) iin);
}
return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public 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_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements io.github.trylovecatch.aidlserver.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getName() throws android.os.RemoteException;
}
Client
调用bindService()
Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");
tIntent.setPackage("io.github.trylovecatch.aidlserver");
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
IBinder
对象不存在,Server
端会回调onBind()
。如果存在,直接回调ServiceConnection
的onServiceConnected
,并传入IBinder
对象。
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends IMyAidlInterface.Stub {
@Override
public String getName() throws RemoteException
{
return "哈哈哈";
}
}
onBind()
里面新建了MyBinder
对象(注意,这个新建不一定必须在onBinder()
里面,可以是成员变量的),而MyBinder
继承 IMyAidlInterface.Stub
。首先会调用:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
将DESCRIPTOR
和实现了IInterface
的IMyAidlInterface
作为(key, value)
存在MyBinder
对象里面。
new MyBinder()
这个过程时,Server
进程 通过Binder驱动
向 Service Manage
r进程 注册服务。在这个绑定的时候,Binder驱动
会通过binder_node
的数据结构保存Binder本地对象
,也就是这个MyBinder
的对象;而SM
,会保存一个Binder
的对应关系Client
的bindService()
,会将Server
的信息,如action
、package
等信息,传递个Binder驱动
;Binder驱动
再将请求传递给SM
,SM
根据上一步保存的对应信息,得到所需要的Server
和Binder
;Binder驱动
程序根据SM
返回的信息,得到对应的Binder本地对象
,但是不会立马返回,而是返回了一个代理对象——BinderProxy
,用binder_ref
的数据结构存储。Client
,回调ServiceConnection
的onServiceConnected
,传进来一个IBinder
对象(如果同一个进程,就是Binder
对象,如果不同进程,就是BinderProxy
),调用:
IMyAidlInterface.Stub.asInterface(service)
我们看一下asInterface()
:
public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {
return ((io.github.trylovecatch.aidlserver.IMyAidlInterface)iin);
}
return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}
这个queryLocalInterface()
,就是根据DESCRIPTOR
去查找已经保存的IInterface
对象,但是上面提过,这个add(key, value)
的过程是在Stub
的构造函数里面,它是在Server
端完成的,所以这里肯定返回null
,所以肯定返回的是Stub.Proxy
对象。
Client
端,拿到了Proxy
对象,然后可以调用Server
端的提供的功能了
mAidlInterface.getName()
这个getName()
方法,是调用Proxy
里面的方法:
@Override
public java.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这里面主要,调用了mRemote.transact()
,传递数据给Binder驱动
,我们知道这个mRemote
是BindProxy
对象,调用这个方法后,Client
线程会挂起。
Binder驱动
,根据BindProxy
对象,找到对应的Binder本地对象
,传递数据给它,并且回调Binder
对象的onTransact()
Server
端,被执行了onTransact()
,如下:
public 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_getName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
调用this.getName()
,也就是Stub
的getName()
,实际上就是调用了MyBinder
的getName()
:
@Override
public String getName() throws RemoteException
{
return "哈哈哈";
}
返回结果给Binder驱动
。
Binder驱动
,再把数据返回给Client
端,Client
端线程被唤醒,显示结果。Messenger
Messenger
,可以在不同进程间传递Message
对象,来实现进程间通信。Messenger
的底层是AIDL
实现的,它是一种轻量级的实现,不需要编写AIDL文件
。Messenger
以串行的方式处理客户端发来的信息,如果大量的信息发送给服务器,服务器仍然只能一个个来处理,所以Messenger
不适用大量的并发请求。Messenger
主要作用是传递数据,只能传递Message
对象;所以,不适合哪些需要跨进程调用服务器端方法的场景。Service
public class MessengerService extends Service {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Messenger tClient = null;
switch (msg.what){
case WHAT_FROM_CLIENT_CONNECTED:
Log.e("service", "message from client: " + msg.getData().getString("msg"));
tClient = msg.replyTo;
if(tClient!=null){
reply(tClient, "链接成功,哈哈哈");
}
break;
case WHAT_FROM_CLIENT_CLICK_BTN:
Log.e("service", "message from client: " + msg.getData().getString("msg"));
tClient = msg.replyTo;
if(tClient!=null){
reply(tClient, "我知道你点击了按钮");
}
break;
default:
super.handleMessage(msg);
break;
}
}
};
private final Messenger mMessenger = new Messenger(mHandler);
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
private void reply(Messenger pClient, String tMsg){
Message tMessage = Message.obtain(null, WHAT_FROM_SERVICE);
Bundle tBundle = new Bundle();
tBundle.putString("reply", tMsg);
tMessage.setData(tBundle);
try {
pClient.send(tMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Manifest.xml
<service android:name=".MessengerService" android:process=":remote" />
Client
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case WHAT_FROM_SERVICE:
Log.e("client", "receive msg from service: " + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
break;
}
}
};
private final Messenger mMessengerClient = new Messenger(mHandler);
private Messenger mMessengerService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessengerService = new Messenger(service);
send(WHAT_FROM_CLIENT_CONNECTED, "hello, hahahaha");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent tIntent = new Intent(this, MessengerService.class);
bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
send(WHAT_FROM_CLIENT_CLICK_BTN, "click client btn");
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
private void send(int pWhat, String tMsg){
if(mMessengerService !=null){
Message tMessage = Message.obtain(null, pWhat);
Bundle tData = new Bundle();
tData.putString("msg", tMsg);
tMessage.setData(tData);
tMessage.replyTo = mMessengerClient;
try {
mMessengerService.send(tMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
代码就这么些,比较简单,源码可以参考:这里
Messenger
的工作原理如下图:
https://www.jianshu.com/p/69e5782dd3c3
DeathRecipient类的作用,当Binder服务端程序挂掉了,通知给Binder客户端程序
在某些场景下,Binder服务端需要回调每一个Binder代理对象对应的回调接口,要先确保Binder代理对象没有死亡,才发送。RemoteCallbackList内部是通过DeathRecipient和匿名Binder来实现的。
ParcelFileDescriptor
MemoryFile
总得来说 ParcelFileDescriptor 和 MemoryFile 的区别有以下几点:
在其他领域的应用方面,ParcelFileDescriptor和MemoryFile也有着性能上的差异,主要取决于两个方面:
数据的大小和类型。
数据的访问方式。
Android中AIDL的使用详解
Android Binder Analysis(2)
Binder学习指南
User space 与 Kernel space
图文详解 Android Binder跨进程通信机制 原理