Binder
Binder是什么?
机制: Binder 是一种进程间通信的机制
驱动: Binder 是一个虚拟物理设备驱动
应用层: Binder 是一个能发起进程间通信的 JAVA 类
Binder 就是 Android 中的血管,在 Android 中我们使用 Activity,Service 等组件都需要和 AMS
( system_server )进行通信,这种跨进程的通信都是通过 Binder 完成。
Activity,Service 等组件和 AMS 不是同一个进程,其实也是多进程通信。
Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。
例如当进程 A 中的 Activity 要向进程 B 中的 Service 通信,这便需要依赖于 Binder IPC 。不仅于此,整
个 Android 系统架构中,大量采用了 Binder 机制作为 IPC (进程间通信, Interprocess
Communication )方案。
也存在部分其他的 IPC 方式,如管道、 SystemV 、 Socket 等
为什么使用Binder
性能方面:
Binder 相对于传统的 Socket 方式,更加高效
Binder 数据拷贝只需要一次,而管道、消息队列、 Socket 都需要 2 次,共享内存方式一次内存拷贝
都不需要,但实现方式又比较复杂。
安全方面:
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如 Socket 通信的 IP 地址是客户
端手动填入,很容易进行伪造
Binder 机制从协议本身就支持对通信双方做身份校检,从而大大提升了安全性
IPC 原理
Binder IPC 实现原理
跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、 Socket 都是内核的一部分。
Binder IPC 正是基于内存映射( mmap )来实现的,但是 mmap() 通常是用在有物理介质的文件系统
上,进程中的用户区域是不能直接和物理设备打交道的。
如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘 –> 内核空间 –> 用户空间) , 通常在
这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,
用内存读写取代 I/O 读写,提高文件读取效。
AIDL
AIDL简介
AIDL( Android 接口定义语言),可以使用它定义客户端与服务端进程间通信( IPC )的编程接口。在 Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分 隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL 就是为了满 足这种需求而诞生的。通过AIDL ,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法, 从而满足进程间通信的需求。 AIDL是用于定义服务端和客户端通信接口的一种描述语言,可以拿来生产 IPC 代码,从某种意义上说 AIDL其实就是一个模板,因为在使用过程中,实际起作用的并不是 AIDL 文件,而是据此生产的一个 Interface的实例代码, AIDL 其实是为了避免我们重复写代码而出现的一个模板。
注意: 只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理
时,您才有必要使用 AIDL 。如果您无需跨不同应用执行并发 IPC ,则应通过实现 Binder 来创建接
口;或者,如果您想执行 IPC ,但 不 需要处理多线程,请使用 Messenger 来实现接口。无论如
何,在实现 AIDL 之前,请您务必理解绑定服务。
使用流程
1. 在 .aidl 文件中定义 AIDL 接口,并将其添加到应用工程的 src 目录下,创建完成之后 rebuild
2. Android SDK 工具会自动生成基于该 .aidl 文件的 IBinder 接口,具体的业务对象实现这个接口, 这个具体的业务对象也是 IBinder 对象,当绑定服务的时候会根据实际情况返回具体的通信对象 (本地还是代理)
3. 将客户端绑定到该服务上,之后就可以调用 IBinder 中的方法来进行进程间通信( IPC )
下面从几个方面介绍 AIDL 的使用
1. 创建 .aildl 文件
2. 具体的业务对象实现基于 .aidl 文件生成的接口
3. 向客户端公开接口
4. 客户端远程调用 5. 验证 AIDL
创建 .aildl 文件
在 AIDL 中可以通过可带参数以及返回值的一个或多个方法来声明接口,参数和返回值可以是任意类 型,AIDL 中支持的数据类型如下: java 的 8 种数据类型: byte 、 short 、 int 、 long 、 float 、 double 、 boolean 、 char
除此之外支持 String 、 charSequence 、 List 、 Map
自定义数据类型
如果参数或返回值类型为 List 或 Map 时:
List 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或自己声明的可打包类
型。可选择将 List 用作 “ 通用 ” 类(例如, List )。另一端实际接收的具体类始终是 ArrayList ,但生
成的方法使用的是 List 接口。
Map 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类
型。 不支持通用 Map (如 Map<String,Integer> 形式的 Map )。 另一端实际接收的具体类始终
是 HashMap ,但生成的方法使用的是 Map 接口。
具体的业务对象实现基于 .aidl 文件生成的接口
打开IPersonAidlInterface.aidl ,在 .aidl 文件中添加具体的业务方法,文件内容如下 :
interface IkeyAidlInterface {
void setName(String name);
void setAge(int money);
String getInfo();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
. 然后,重新 rebuild project , Android SDK 工具会在相应的目录生成对应的与 .aidl 文件同名的 .java
接口文件,具体目录如下:
那么这个业务要体现在什么地方呢,从上面可知 Stub 是一个抽象类,那么它所提供的具体业务必
然需要一个具体的实现类来完成,下面实现这个具体的业务类,具体如下
public class IKeyimpl extends IkeyAidlInterface.Stub{
String name;
int money;
@Override
public void setName(String name) throws RemoteException {
this.name=name;
}
@Override
public void setAge(int money) throws RemoteException {
Intent intent = new Intent();
intent.setClass(KeyService.this,MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
this.money=money;
}
@Override
public String getInfo() throws RemoteException {
return "交易账户:"+name+"金额是:"+money;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
向客户端公开接口
创建一个 Service 以便对外提供具体的业务,具体如下:
public class KeyService extends Service {
public KeyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new IKeyimpl();
}
}
也可以将Stub 抽象类与service复合写如下:
public class KeyService extends Service {
public KeyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new IKeyimpl();
}
public class IKeyimpl extends IkeyAidlInterface.Stub{
String name;
int money;
@Override
public void setName(String name) throws RemoteException {
this.name=name;
}
@Override
public void setAge(int money) throws RemoteException {
Intent intent = new Intent();
intent.setClass(KeyService.this,MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
this.money=money;
}
@Override
public String getInfo() throws RemoteException {
return "交易账户:"+name+"金额是:"+money;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
}
客户端远程调用
通过上面几步完成了服务的搭建,并将服务运行在独立进程中,下面主要就是客户端的具体调用了,具 体实现参考如下:
public class MainActivity extends AppCompatActivity {
private Button button,button2,button3,button4;
TextInputEditText textField,edt_one;
private IkeyAidlInterface ikeyAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=findViewById(R.id.button);
button2=findViewById(R.id.button2);
button3=findViewById(R.id.button3);
button4=findViewById(R.id.button4);
textField=findViewById(R.id.textField);
edt_one=findViewById(R.id.edt_one);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.day36start2","com.example.day36start2.KeyService"));
bindService(intent,conn,BIND_AUTO_CREATE);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
ikeyAidlInterface.setName("a123456");
ikeyAidlInterface.setAge(100);
String zys=ikeyAidlInterface.getInfo();
Log.i("take", zys);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ikeyAidlInterface = IkeyAidlInterface.Stub.asInterface(service);
Log.i("TAG" , ""+ikeyAidlInterface);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
在新版本要在AndroidManifest.xml中写上如下代码:
<queries>
<package android:name="com.example.day36start2" />
</queries>