IPC机制之AIDL传递基础类型数据(一) IPC机制之Messenger示例(二) IPC机制之AIDL传递Parcelable(三)
序言
前面的3篇文章文章实现了多进程的通讯,但是都只完成了客户端调用服务端的方法或者客户端发送消息和服务端通信,下面我们介绍下AIDL中的观察者模式,客户端绑定服务端成功狗,回调客户端的方法。
以上代码都在在 IPC机制之AIDL传递Parcelable 文章的基础上实现的,所以只展示不同代码,后面会附上完整代码。
Server#IOnNewBookArrivedListener.aidl
package
com.aidd.sample
import
com.aidd.sample.Book
interface IOnNewBookArrivedListener {
void onNewBookArrived(
in Book newBook)
}
Server#IBookManager.aidl
package
com.aidd.sample
import
com.aidd.sample.Book
import
com.aidd.sample.IOnNewBookArrivedListener
interface IBookManager {
List<Book> getBookList()
void addBook(
in Book book)
void registerListener(IOnNewBookArrivedListener listener)
void unregisterListener(IOnNewBookArrivedListener listener)
}
Server#BookManagerService.java
package com.aidd.sample;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class BookManagerService extends Service {
private AtomicBoolean mIsServiceDestroyed =
new AtomicBoolean(
false);
private CopyOnWriteArrayList<Book> mBookList =
new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
new RemoteCallbackList<>();
private String TAG =
"info";
@Override
public void onCreate() {
super.onCreate();
mBookList.add(
new Book(
1,
"Android"));
mBookList.add(
new Book(
2,
"iOS"));
new Thread(
new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(
true);
super.onDestroy();
}
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 registerListener(IOnNewBookArrivedListener listener)
throws RemoteException {
mListenerList.register(listener);
int count = mListenerList.beginBroadcast();
Log.i(TAG,
"registerListener success, count-->>" + count);
mListenerList.finishBroadcast();
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener)
throws RemoteException {
mListenerList.unregister(listener);
int count = mListenerList.beginBroadcast();
Log.i(TAG,
"unregisterListener success, count-->>" + count);
mListenerList.finishBroadcast();
}
};
@Nullable
@Override
public IBinder
onBind(Intent intent) {
return mBinder;
}
private void onNewBookArrived(Book book)
throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
Log.i(TAG,
"onNewBookArrived, notify booklist-->>" + mBookList.size());
for (
int i =
0; i < N; i++) {
IOnNewBookArrivedListener arrivedListener = mListenerList.getBroadcastItem(i);
if (arrivedListener !=
null)
arrivedListener.onNewBookArrived(book);
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.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();
}
}
}
}
}
RemoteCallbackList 是一个键值对,Key是Binder,value就是回调的callback,目的是为了记录注册了回调的客户端。
Client#MainActivity.java
package sample.aidl.com.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.aidd.sample.Book;
import com.aidd.sample.IBookManager;
import com.aidd.sample.IOnNewBookArrivedListener;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private IBookManager iBookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startIPC(View view) {
Intent intent =
new Intent();
intent.setComponent(
new ComponentName(
"com.aidd.sample",
"com.aidd.sample.BookManagerService"));
this.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection =
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> bookList = iBookManager.getBookList();
Log.i(
"info",
"列表类型" + bookList.getClass().getCanonicalName());
Log.i(
"info",
"数据查询结果" + bookList.toString());
iBookManager.registerListener(mListener);
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
iBookManager =
null;
}
};
Handler handler =
new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i(
"info",
"recive new book");
break;
}
}
};
private IOnNewBookArrivedListener mListener =
new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook)
throws RemoteException {
handler.obtainMessage(
1, newBook).sendToTarget();
}
};
@Override
protected void onDestroy() {
if (iBookManager !=
null && iBookManager.asBinder().isBinderAlive()) {
try {
iBookManager.unregisterListener(mListener);
}
catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
Q: Client#MainActivity.java中为什么IOnNewBookArrivedListener回调方法使用Handle? A: 因为客户端的回调方法运行在客户端的Binder线程池中,即子线程,所以无法直接更新UI,但是onServiceConnected和onServiceDisconnected是运行在UI线程的。
Q: Server#BookManagerService.java中的mBinder方法是运行在哪里? A: 客户端调用服务端的方法也是运行在Binder线程池中的,只是是运行在服务端的Binder线程池中,所以这里面的方法是非UI线程中。
Q: 服务端binder提供的方法是不是可以执行耗时操作? A: 由于Binder中的方法是在Binder线程池中,所以是可以执行耗时操作的,但是如果客户端是在UI线程中调用的这些方法,会导致客户端长时间收不到回调而卡死,出现ANR。同理,如果服务端如果在主线程中调用客户端的回调方法时,如果是客户端回调是耗时操作,服务端也会出现ANR。
源码下载