Binder学习(四)利用AIDL、Messenger实现IPC
概述
在利用Binder进行IPC的时候,会经常需要创建一个Server端,Android中通常的实现是利用Service来实现,所以再进行IPC之前先了解之前先复习一下Service:
启动方式
startService
Intent intent = new Intent();
intent.setClass(this, MyService.class);
startService(intent);
bindService
Intent intent = new Intent();
intent.setClass(this, MyService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//绑定成功的回调
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//断开连接的回调
}
}, Context.BIND_AUTO_CREATE);
生命周期
常见方法回调时机
onCreate()
在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBind()之前,我们可以在这个方法中进行一些一次性的初始化工作。
onStartCommand()
当其他组件通过startService()方法启动service时,此方法将会被调用。
onBind()
当其他组件通过bindService()方法与service相绑定之后,此方法将会被调用。这个方法有一个IBinder的返回值,这意味着在重写它的时候必须返回一个IBinder对象,它是用来支撑其他组件与service之间的通信的——另外,如果你不想让这个service被其他组件所绑定,可以通过在这个方法返回一个null值来实现。
onUnbind()
当调用UnBindService的时候 ,此方法会被调用
onDestroy
这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。
销毁方式
startService方式启动
startService()启动,stopService()销毁的生命周期如下:
->onCreate()->onStartCommand()->Service running-> onDestroy()
bindService方式启动
bindService()启动,unbindService()销毁的生命周期如下:
->onCreate()->onStartCommand()->Service running-> onDestroy()
先startService再bindService
- 如果结束只调用unbindService(),那么只会执行到onUnbind(),将不会执行onDestroy():->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()。
- 如果在unbindService后,在调用stopService(),那么:->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()->onDestroy() 。
正文
startService
其实starService跟Binder机制没有太大关系,通过此方式虽然可以启动一个本地或者远程的Service,但是我们拿不到Binder对象,不能直接通过AIDL的方式进行远程通信,只能通过其它的IPC方式进行通信,即时如此,这种方式启动的Service还是有一个方法需要注意一下,就是onStartCommand。
onStartCommand
@Override public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
调用时机
启动service的时候,onCreate方法只有第一次会调用,onStartCommand每次都被调用。onStartCommand会告诉系统如何重启服务,如判断是否异常终止后重新启动,在何种情况下异常终止 启动服务时依次执行onCreate,onStartCommand;如果在系统显示调用stopService和stopSelf之前终止服务,service再次重启,onStartCommand会被调用,重启服务时依次执行onStartCommand。
返回值
我们发现onStartCommand这个方法有个int类型返回值,实际上有四种类型,都是定义在Service中的静态常量:
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
public static final int START_STICKY = 1;
public static final int START_STICKY_COMPATIBILITY = 0;
下面依次解释下这几种返回值的含义:
- 1):START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
- 2):START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
- 3):START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
- 4):START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
flags
flags表示启动服务的方式:通过startService启动时为0;onStartCommand返回为START_FLAG_REDELIVERY, or START_FLAG_RETRY.
- START_FLAG_RETRY:表示服务之前被设为START_STICKY,则会被传入这个标记。
- START_FLAG_REDELIVERY:如果你实现onStartCommand()来安排异步工作或者在另一个线程中工作, 那么你可能需要使用START_FLAG_REDELIVERY来让系统重新发送一个intent。这样如果你的服务在处理它的时候被Kill掉, Intent不会丢失.
bindService
前面说了那么多,bindService才是重点,通过bindService启动的Service会调用onBind方法,我们现在 分析一下如何拿到我们想要的Binder对象,因为不管是Client还是Server,想要通过AIDL进行IPC通信,就必须拿到一个Binder对象,但是如果通过Messenger的话就不需要了,因为Messenger底层自己封装了AIDL。
其实通过前面的分析,很容易看出Client在进行bindService的时候传入了一个ServiceConnection,当跟Server端连接成功的时候会在onServiceConnected中返回一个Binder对象,那么这个Binder对象是从哪儿来的呢?结合Server端的onBind方法,就很明显了,这个Binder对象就是服务端传递过来的。其实通过之前的AIDL分析,也很容易能够判断出来,只要是通过Binder机制进行IPC通信的,无论是Client还是Server端,都会涉及到Binder,我们只要找到了Binder,整个流程就很清晰了,下面分别描述一下Client跟Server中关于Binder的两个方法
CLient的bindService
ServiceConnection
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Client通过onServiceConnected可以拿到服务端返回的Binder对象,因为Binder实现了IBinder接口,返回的是IBinder。
Server的onBind
@Override
public IBinder onBind(Intent intent) {
return null;
}
Server通过onBind返回IBinder对象,默认的而实现为null,我们有多种方式可以实现Binder,通过继承Binder类,AIDL以及Messenger都可以做到,下面简要说明一下自定义Binder,Messenger跟AIDL下面会重点进行讲解:
继承Binder
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
比较简单,这种方式一般用在Service跟Activity进行通信的过程中,进行方法调用,大多数是在同一个进程中,我们看到在LocalBinder 中定义了getService方法,可以获取到Service的实例,比如我在项目中的定位是放在Service中的,但是拿到定位数据之后需要在Activity中显示地域切换的对话框,所以Service就需要跟Activity进行交互,Service可以调Activity的方法,同样由于Activity在Binder中拿到了Service的引用,也可以调用Service的中的方法。
使用AIDL进行IPC
Client端代码
ServiceConnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mPeopleManager = PeopleManager.Stub.asInterface(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
开始连接
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, AIDLService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
addPeople
public void addBook(View view) {
//如果与服务端的连接处于未连接状态,则尝试连接
if (!mBound) {
attemptToBindService();
return;
}
if (mPeopleManager == null)
return;
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
try {
mPeopleManager.addPeople(people);
Log.e(getLocalClassName(), people.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
getPeople
public void getPeople(View view) {
//如果与服务端的连接处于未连接状态,则尝试连接
if (!mBound) {
attemptToBindService();
Log.d("client-->", "正在连接Server");
return;
}
if (mPeopleManager == null)
return;
try {
Toast.makeText(this, String.valueOf(mPeopleManager.getPeople().size()), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
Server端代码
创建一个Stub对象
//由AIDL文件生成的PeopleManager
private final PeopleManager.Stub mPeopleManager = new PeopleManager.Stub() {
@Override
public List<People> getPeople() throws RemoteException {
synchronized (this) {
if (mPeoples != null) {
return mPeoples;
}
return new ArrayList<>();
}
}
@Override
public void addPeople(People book) throws RemoteException {
synchronized (this) {
if (mPeoples == null) {
mPeoples = new ArrayList<>();
}
if (book == null) {
Log.e(TAG, "People is null in In");
}
mPeoples.add(book);
}
}
};
onBind进行返回
@Override
public IBinder onBind(Intent intent) {
return mPeopleManager;
}
开启远程线程
<service
android:name=".AIDLService"
android:enabled="true"
android:exported="true"
android:process=":server">
</service>
测试
使用Messenger进行IPC
Client端代码
创建一个Handler
public class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESS_FROM_SERVER:
//接收服务器传过来的值
Bundle bundle = msg.getData();
int peopleSize = bundle.getInt("server");
Toast.makeText(MessengerActivity.this, String.valueOf(peopleSize), Toast.LENGTH_SHORT).show();
break;
}
}
}
创建一个Messenger
Messenger messenger = new Messenger(new ClientHandler());
ServiceConnection
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBound = true;
mService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
isBound = false;
}
};
开始连接
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, MessengerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
addPeople
public void addPeople(View view) {
//如果与服务端的连接处于未连接状态,则尝试连接
if (!isBound) {
attemptToBindService();
return;
}
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
Message message = Message.obtain(null, MESS_ADD_PEOPLE);
Bundle bundle = new Bundle();
bundle.putParcelable("people", people);
message.setData(bundle);
//Messenger用来接收服务端发来的信息
message.replyTo = messenger;
try {
//将Message发送到服务端
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
getPeople
public void getPeople(View view) {
//如果与服务端的连接处于未连接状态,则尝试连接
if (!isBound) {
attemptToBindService();
Log.d("client-->", "正在连接Server");
return;
}
Message message = Message.obtain(null, MESS_GET_PEOPLE);
//Messenger用来接收服务端发来的信息
message.replyTo = messenger;
try {
//将Message发送到服务端
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Server端代码
创建一个Handler
用来收发消息,由于我们是在一个应用里面的开启的多进程,所以这边接收到Client的请求,不能直接将其转化为People对象,因为People对象属于应用进程,而MessengerService属于另外一个进程,是不能共享这个数据的,这里采用了收到消息后,用一个int 类型的数据来模拟集合的数量
mHandler = new Handler() {
@Override
public void handleMessage(Message msgFromClient) {
super.handleMessage(msgFromClient);
//获取一个新消息
Message replyToClient = Message.obtain(msgFromClient);
switch (msgFromClient.what) {
//根据Message.what来判断执行服务端的哪段代码
case MESS_ADD_PEOPLE:
size += 2;
break;
case MESS_GET_PEOPLE:
replyToClient.what = MESS_FROM_SERVER;
Bundle serverBundle = new Bundle();
serverBundle.putInt("server", size);
replyToClient.setData(serverBundle);
try {
msgFromClient.replyTo.send(replyToClient);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
创建一个Messenger
messenger = new Messenger(mHandler);
onBind进行返回
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
开启远程线程
<service
android:process=":server"
android:name=".MessengerService"/>
测试结果
总结
通过利用AIDL跟Messenger来实现Android应用层的IPC,可以更加方便的帮助我们理解Binder机制,当然Android系统还可以利用其它的方式来进行IPC,例如通过文件共享,intent传值等,下面简单就这些方式做一个对比:
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Intent | 简单易用 | 只能传输Bundle所支持的数据类型 | 四大组件间的进程间通信 |
文件共享 | 简单易用 | 不适合高并发 | 简单的数据共享,无高并发场景 |
AIDL | 功能强大,支持一对多并发实时通信 | 使用稍微复杂,需要注意线程同步 | 复杂的进程间调用,Android中最常用 |
Messenger | 比AIDL稍微简单易用些 | 比AIDL功能弱,只支持一对多串行实时通信 | 简单的进程间通信 |
ContentProvider | 强大的数据共享能力,可通过call方法扩展 | 受约束的AIDL,主要对外提供数据线的CRUD操作 | 进程间的大量数据共享 |
RemoteViews | 在跨进程访问UI方面有奇效 | 比较小众的通信方式 | 某些特殊的场景 |
Socket | 跨主机,通信范围广 | 只能传输原始的字节流 | 常用于网络通信中 |
进程间通信的方式
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Intent | 简单易用 | 只能传输Bundle所支持的数据类型 | 四大组件间的进程间通信 |
文件共享 | 简单易用 | 不适合高并发 | 简单的数据共享,无高并发场景 |
AIDL | 功能强大,支持一对多并发实时通信 | 使用稍微复杂,需要注意线程同步 | 复杂的进程间调用,Android中最常用 |
Messenger | 比AIDL稍微简单易用些 | 比AIDL功能弱,只支持一对多串行实时通信 | 简单的进程间通信 |
ContentProvider | 强大的数据共享能力,可通过call方法扩展 | 受约束的AIDL,主要对外提供数据线的CRUD操作 | 进程间的大量数据共享 |
RemoteViews | 在跨进程访问UI方面有奇效 | 比较小众的通信方式 | 某些特殊的场景 |
Socket | 跨主机,通信范围广 | 只能传输原始的字节流 | 常用于网络通信中 |