1.Binder
Binder是什么(引用Carson_Ho博客解释):
- 从机制、模型角度来说:Binder是一种Android中实现跨进程通信的方式。
- 从模型的结构、组成来说:Binder是一种虚拟的物理驱动
- 从Android代码来说:Binder是一个类,实现了IBinder接口。
1.1 Linux的进程空间划分
在Linux的系统中将一个进程分为 用户空间 & 内核空间(kernel)。
- 用户空间:在不同进程间,用户空间的数据不可共享。
- 内核空间:在不同进程间,内核空间可共享。即所有的进程都共享一个内核空间。
- android中所有的进程都是相互独立,隔离的,即一个进程不能直接操作或访问另一个进程的数据。要实现跨进程通信,需要通过所有进程共享的内核空间来实现。
1.2 Binder跨进程通信机制模型
Binder跨进程同通信机制模型基于Client - Server模式
模型组成说明图:
角色 | 作用 | 备注 |
---|---|---|
Client进程 | 使用服务进程 | android客户端 |
Server进程 | 提供服务的进程 | android 服务端 |
ServiceManager进程 | 管理Service注册与查询(将字符形式的名字转化 为Client中对该Binder的引用) | 类似路由器 |
Binder驱动 | 连接Service进程,Client进程和ServiceManager的桥梁,具体作用为: 1.传递进程间的数据:通过内存映射传递进程间的数据 2.实现线程控制:采用Binder的线程池,并由Binder驱动自生管理 | Binder驱动持有每个Server进程在内核空间中的Binder实体,并给Client进程提供Binder实体的引用。 |
1.3 Binder驱动的作用&原理:
Binder工作流程
- Binder驱动创建一块接收缓存区;
- 实现地址映射关系:根据需要映射的接收进程信息,实现内核缓存区和接收进程用户空间地址同时映射到一个Binder创建的一个共享接收缓存区中。
- 发起进程通过系统调用copy_from_user发送数据到虚拟内存区域(数据拷贝1次)
- 由于内核缓存区与接受进程用户空间地址存在映射关系(同时映射到Binder创建的接受缓存区中),故发起进程发送的数据到内存空间的数据也发送到了用户接受进程的用户空间
示意图:
1.4 Android进程通信流程说明
- Server进程注册服务:Service进程向Binder驱动发起注册服务请求,Binder驱动将注册请求转发给ServiceManage进程,ServiceManage添加该Server进程。
- Client进程获取服务:Client向Binder驱动发去获取服务的请求,传递需要获取服务的名称,Binder通过ServiceManage找到该服务进程,同时通过Binder驱动将服务进程信息返回给Client进程。
- 使用服务(进程间通信):1.Binder驱动在内核空间创建缓存区实现server进程与clietn进程的内存地址映射;2.Client进程将参数数据发送到Server进程;3.Server进程根据Client进程的要求调用相应的方法;4.Server进程将目标方法的执行结果返回给Client进程。
Binder请求的线程管理
Binder模型的线程管理由Binder驱动的线程池,并由Binder驱动自生管理。一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的线程。
ServiceManage
ServiceManage本身是一个进程,它的作用是将字符形式的Binder名字转化为Client中对该Binder的引用。当Server进程通过向ServiceManager注册的过程也是进程间的通信。此时server进程座位Client端,ServiceManager座位service端也,他们之间也是通过Binder来通信。ServiceManager进程中的Binder实体在其它所以的进程中都是通过"0"这个字符来获得其引用。
Binder架构
引用Jeanboydev博客
Binder机制图解
引用Jeanboydev博客
AIDL跨进程通信
实现连个app跨进程通信流程:
- step1 :编写两个app通信的实体类(如果是基本类型就不用写,实体类要实现Parcelable接口。
- step2:编写aidl实体类文件和接口文件。实体类文件名与相应的实体名相同。
//Person.aidl package com.aoy.learn.source.bean; parcelable Person; 复制代码
aidl接口文件:
package com.aoy.learn.source.bean;
import com.aoy.learn.source.bean.Person;
/**
* Created by drizzt on 2018/6/1.
*/
interface IMyAidl{
/**
* 传参时除了Java基本类型以及String,CharSequence之外的类型
* 其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
*/
void addPerson(in Person person);
List<Person> getPersonList();
}
复制代码
- step3: Make Proteced
- step4: 编写service,并且在AndroidManifest.xml注册这个service,指明android:exported="true"
public class MyService extends Service {
private List<Person> mPersons = new ArrayList<>();
private IBinder mIBinder = new IMyAidl.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
}
@Override
public List<Person> getPersonList() throws RemoteException {
return mPersons;
}
};
@Override
public void onCreate() {
super.onCreate();
mPersons.add(new Person());
mPersons.add(new Person());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
}
复制代码
- step5: 在另外一个移植aidl文件和实体类,移植的实体类的包名要和原始的包名一致,移植的aidl文件名和包名也要一致;
- step6:在另外一个app中连接这个service并通过binder代理通信;
//另外一个app中的mainActivity
public class MainActivity extends AppCompatActivity {
final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.btn)
Button btn;
MyServiceConnector mServiceConnector;
IMyAidl myAidl;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
init();
}
private void init(){
try {
mServiceConnector = new MyServiceConnector();
toConneRemoteService();
RxView.clicks(btn)
.subscribe(o -> handleRomoteBinde());
}catch (Exception e){
e.printStackTrace();
}
}
private void handleRomoteBinde(){
if(myAidl != null){
try {
List<Person> mList = myAidl.getPersonList();
Log.i(TAG,"remote communications is successful: person list size:" + mList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private void toConneRemoteService(){
Intent intent = new Intent();
intent.setClassName("com.aoy.learn.source","com.aoy.learn.source.service.MyService");
bindService(intent,mServiceConnector,BIND_AUTO_CREATE);
}
class MyServiceConnector implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG,"Remote service has connected");
myAidl = IMyAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
复制代码
执行结果:
06-04 18:19:15.592 4023-4023/com.aoy.service.source I/MainActivity: remote communications is successful: person list size:2
复制代码
接口文件中传参tag:int out,inout
int,out,intou 代表aidl进程通信中的数据流向,只能修饰aidl接口文件中方法传参,不能修饰方法的返回值。
- in表示传参对象只能由客户端流向服务端,服务端会接受到完整的传参对象,但是在服务端对传入对象做任何改变不会影响客户端的原有传入对象;
- out表示传参对象只能由服务端传入客户端,不管客户端传入什么对象,在相应的服务端都只能接受到传入对象的null值,但是如果服务端对这个传入值做任何修改,客户端原有的对象将会发生相应的改变;
- inout表示传参数据可以在服务器和客户端之间双向流通,服务端将接受到客户端传入对象的完整值,同时服务端对传入对象的改变将影响客户端的值。
Messenger实现进程之间的通信
利用Messenger实现两个app间的通信步骤:
- step1:在提供服务的app工程里编写service类,这个service有一个Messenger属性,service的Binder由这个messenger提供。
public class BindMineServiece extends Service {
public static final String TAG = "BindMineServiece";
Messenger messenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
// 由handler处理app间的通信数据
if(AccountHandler.getInstance().getLoginUser() != null && AccountHandler.getInstance().getAccessToken() != null){
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putInt("ret_data",1);
message.setData(bundle);
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
复制代码
- step2:在AndroidManifest.xml中注册这个service,并设置其属性 android:exported="true"
- step3:在另外一个app中通过包名和类名连接这个service,并通过Messenger通信。
private void bindMineService(String appId, String serviceName) {
if (serviceConnection == null) {
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message message = Message.obtain();
message.replyTo = replyMessenger;
try {
messenger.send(message);
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//TODO 解除绑定
Log.i("serviceConnection", "解除绑定");
}
};
}
//replyMessenger负责接收remote service的数据
if (replyMessenger == null) {
replyMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
msg.getData().get("ret_data");
}
});
}
try {
Intent intent = new Intent();
intent.setComponent(new ComponentName(appId, serviceName));
getActivity().bindService(intent, serviceConnection, BIND_AUTO_CREATE);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
Messenger在服务端不能处理多线程,AIDL可以处理多线程