上一节我们介绍了使用Messenger来进行进程间通信的方法,可以发现,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。AIDL是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装,从而方便上层的调用而已。在上一节中,我们介绍了Binder的概念,大家对Binder也有了一定的了解,在Binder的基础上我们可以更加容易地理解AIDL。这里先介绍使用AIDL来进行进程间通信的流程,分为服务端和客户端两个方面。
1、服务端
服务端首先要创建一个Service用来监听客户端的连接请求,
然后创建一个AIDL文件,
将暴露给客户端的接口在这个AIDL文件中声明,
最后在Service中实现这个AIDL接口即可
2、客户端
客户端所要做事情就稍微简单一些,首先需要绑定服务端的Service,
绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,
接着就可以调用AIDL中的方法了
上面描述的只是一个感性的过程,AIDL的实现过程远不止这么简单,接下来会对其中的细节和难点进行详细介绍,并完善我们在Binder那一节所提供的的实例。
3、AIDL接口的创建
首先看AIDL接口的创建,如下所示,我们创建了一个后缀为AIDL.的文件,在里面声明了一个接口和两个接口方法。
// IBookManager.aidl
package com.example.chapter_2.aidl;
import com.example.chapter_2.aidl.Book;//需要显示的导入这个Book类
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);//aidl文件中这个自定义的Book一定要加 in , 否则会报语法错误
}
// Book.aidl
package com.example.chapter_2.aidl;
parcelable Book; //注意是小写
4、远程服务端Service的实现代码
上述我们讲了如何定义一个AIDL的接口,接下来我们就需要实现这个接口了。我们先创建一个Service,称为BookManagerService,代码如下:
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//步骤2:创建一个mBinder对象,实现了aidl中抽象类Stub定义的方法
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;//具体的实现逻辑
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);//具体的实现逻辑
}
};
@Override
public void onCreate() {
super.onCreate();
//步骤1:onCreate中初始化2本图书
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(1,"IOS"));
}
@Override
public IBinder onBind(Intent intent) {
//步骤3:客户端绑定成功就将这个实现了接口的Binder对象返回给客户端
return mBinder;
}
}
注册服务
<service
android:name=".BookManagerService"
android:process=":remote" >
</service>
5、客户端的实现代码
客户端的实现就比较简单了,我们绑定远程服务,绑定成功之后返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务端的远程方法,代码如下所示:
public class BookManagerActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//将服务端返回的binder对象转换成客户端本地的aidl接口
IBookManager bookManager = IBookManager.Stub.asInterface(service);
//就可以通过这个bookManager Binder对象来调用到其它进程中的方法
try {
List<Book> list = bookManager.getBookList();
Log.i(TAG, "list Type :" + list.getClass()。getCanonicalName());
Log.i(TAG, "list string : " + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
//绑定服务
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);//解绑服务
}
}
这样一个完整的AIDL的IPC过程就完成了。接着看难点。
6、如何去订阅远程服务?
// IOnNewBookArrivedListener.aidl (服务端)
package com.example.chapter_2.aidl;
import com.example.chapter_2.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
除了要新增加一个AIDL外,还需要在原有的接口中添加两个新的方法
// IBookManager.aidl(服务端)
package com.example.chapter_2.aidl;
import com.example.chapter_2.aidl.Book;
import com.example.chapter_2.aidl.IOnNewBookArrivedListener;//自定义的aidl也需要显示的导入
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
//新增加的2个方法
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
接下来,我们的服务端也是要稍微的修改一下,每隔5s向感兴趣的用户提醒
//BookManagerService(服务端)
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
//定义一个listener的list,存储所有注册用户的listener
private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
//实现IBookManager中的registerListener方法
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
if(!mListenerList.contains(listener)){
mListenerList.add(listener);
}else {
Log.i(TAG,"already exists");
}
Log.i(TAG,"registerListener size:" + mListenerList.size());
}
//unregisterListener
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
if(mListenerList.contains(listener)){
mListenerList.remove(listener);
Log.i(TAG,"remove listener");
}else {
Log.i(TAG,"can not remove listener");
}
Log.i(TAG,"unregisterListener size:" + mListenerList.size());
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(1,"IOS"));
new Thread(new ServiceWorker()).start();//开启一个子线程,每隔5秒添加一本书
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);//设置标记位,结束子线程
super.onDestroy();
}
private class ServiceWorker implements Runnable{
@Override
public void run() {
while (!mIsServiceDestoryed.get()){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size()+1;
Book newBook = new Book(bookId,"new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
private void onNewBookArrived(Book book) throws RemoteException{
mBookList.add(book);//每隔5秒向list中添加一本书
Log.i(TAG,"onNewBookArrived size:" + mListenerList.size());
for (int i = 0; i < mListenerList.size(); i++) {
IOnNewBookArrivedListener listener = mListenerList.get(i);//拿到客户端传递过来的listener
Log.i(TAG,"listener: "+ listener);
listener.onNewBookArrived(book);//回调到客户端的onNewBookArrived方法,并且把图书作为参数传递给每个订阅的用户
}
}
}
最后,我们还需要修改一下客户端的代码,主要是两方面,首先是客户端要注册IOnNewBookArrivedListener到远程的服务器,这样当有新书时服务端才能通知客户端,同时在我们要在退的Activity的时候接触这个注册;另一方面,当有新书的时候,服务端会回调客户端的onNewBookArrived方法,但是这个方法是在客户端的Binder线程池中执行的,因此,为了便于进行UI操作,我们需要有一个Handler可以将其切换到客户端的主线程去执行,这个原理在Binder中已经做了分析了,这里不多说,客户端代码修改如下:
//BookManagerActivity (客户端)
public class BookManagerActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
public static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;//用于接收远程binder转化成客户端AIDL
private Handler mHhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.i(TAG, "recieve new book :" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
List<Book> list = bookManager.getBookList();
Log.i(TAG, "list Type :" + list.getClass().getCanonicalName());
Log.i(TAG, "list string : " + list.toString());
Book newBook = new Book(3, "Android进阶");
bookManager.addBook(newBook);
Log.i(TAG, "add Book: " + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query list : " + newList.toString());
//注册
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
}
};
//定义一个客户端的Binder对象 listener,用于传递给服务端注册回调
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book newBook) throws RemoteException{
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
try {
//解注册
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(mConnection);
super.onDestroy();
}
}
7、解决解注册失败问题(不是同一个listener)
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
然后修改registerListener 和unregisterListener这两个接口的实现
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.registener(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregistener(listener);
}
怎么样,使用起来是不是很简单,接下来我们修改onNewBookArrived方法,当有新书的时候,我们就要通知所有已注册的listener
private void onNewBookArrived(Book book) throws RemoteException{
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcast(i);
if(i != null){
l.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();//必须配对使用
}