Binder
Binder是Android中的一个类,继承了IBinder接口,是一种跨进程通信方式。
从Framework角度看:Binder是ServiceManager连接各种Manager(ActivityManager WindowManager等)和相应的ManagerService的桥梁;当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过该对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。
通过Service利用AIDL进行通信。
AIDL(Android Interface Definition Language)是一种接口定义语言,由于Android的每个进程都运行在独立的虚拟机中,所以进程之间通信会比较麻烦。我们可以利用AIDL将一个进程的数据拆分成Android系统可识别的数据单元,然后系统再重新将数据单元合成传递给另一个进程。这样就实现了进程间的通信。
实现功能:服务端有一个书库,客户端可以向服务端添加书本、查阅书目;服务端可以向注册了监听的客户端推送新书(客户端可以随时解除注册的监听)。在此基础上,服务端还会对客户端进行权限检查
创建 Book类实现Parcelable接口
只有实现了该接口才能在不同的进程间传递
创建Book.aidl
用于声明Book类
package cqupt.second.aidl;
parcelable Book;
创建IOnNewBookArrivedListener.aidl
让客户端实现监听的接口package cqupt.second.aidl;
import cqupt.second.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
创建IBookManager.aidl文件
暴露服务端的方法给客户端,暴露客户端的监听给服务端package cqupt.second.aidl;
import cqupt.second.aidl.Book;
import cqupt.second.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
服务端的实现代码:
- 使用CopyOnWriteArrayList保存Book是因为其支持并发的读写
- RemoteCallbackList专门提供用于删除进程间listener的接口
- 权限检查:checkCallingOrSelfPermission
- 服务端和客户端如果是两个工程,则在Service的onBind方法中无法验证客户端的权限。原因是onBind方法并不是一个Binder调用,它运行在服务端的UI线程中,故在onBind中只能验证服务端的权限,然而这是没有意义的。推荐在onTransact方法中对客户端进行权限验证。
public class BookManagerServicec extends Service {
private static final String TAG = "BookManagerServicec";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();
private AtomicBoolean isServicceDestoryed = new AtomicBoolean(false);
@Nullable
@Override
public IBinder onBind(Intent intent) {
//权限的检查,此处的不通过,直接不能完成绑定
int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "onbind check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
mBookList.add(new Book(3, "Java"));
new Thread(new ServiceWorker()).start();
}
//Binder需要实现AIDL文件中的接口的方法,以便该Binder对象给客户端让客户端调用该方法
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 {
mListeners.register(listener);
}
//解除注册监听
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListeners.unregister(listener);
}
//设置权限的第二种方式。此处权限检测失败不会执行AIDL中的方法
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
String packageName = null;
String[] packagesForUid = getPackageManager().getPackagesForUid(getCallingUid());
if (packagesForUid != null && packagesForUid.length > 0) {
packageName = packagesForUid[0];
}
if (!packageName.startsWith("cqupt.second")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
//把新书发送给注册了监听的客户端
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
//注意RemoteCallbackList的使用方式
int i = mListeners.beginBroadcast();
for (int j = 0; j < i; j++) {
IOnNewBookArrivedListener item = mListeners.getBroadcastItem(j);
if (item != null) {
item.onNewBookArrived(book);
}
}
mListeners.finishBroadcast();
}
//服务端模拟自动增加新书
private class ServiceWorker implements Runnable{
@Override
public void run() {
while (!isServicceDestoryed.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();
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
isServicceDestoryed.set(true);
}
}
客户端代码实现
public class BookManagerActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private IBookManager remoteBookManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
//在此方法中绑定远程服务
Intent intent = new Intent(this, BookManagerServicec.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过IBookManager.Stub.asInterface(service)方法将服务器返回的Binder转换成AIDL接口
IBookManager bookManager = IBookManager.Stub.asInterface(service);
//另一个AIDL接口
remoteBookManager = bookManager;
try {
//设置Binder死亡监听
remoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
List<Book> list = bookManager.getBookList();
Log.d(TAG, "query book list,list type:" + list.getClass().getCanonicalName());
Log.d(TAG, "query book list:" + list.toString());
Book book = new Book(4, "Android 开发艺术之旅");
bookManager.addBook(book);
List<Book> newList = bookManager.getBookList();
Log.d(TAG, newList.toString());
//客户端注册监听到远程服务端
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
remoteBookManager = null;
}
};
//服务端有新书更新时回调客户端的此方法,该方法在客户端的Binder线程池中执行,需要更新UI则需要Handler
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
Message message = new Message();
message.what = 1;
message.obj = newBook;
mHandler.sendMessage(message);
}
};
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "receive new book:" + msg.obj);
break;
}
super.handleMessage(msg);
}
};
//设置死亡Binder死亡监听,如果死亡,重新绑定
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
if (remoteBookManager == null)
return;
remoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
remoteBookManager = null;
// TODO:这里重新绑定远程Service
}
};
@Override
protected void onDestroy() {
if (remoteBookManager != null && remoteBookManager.asBinder().isBinderAlive()) {
try {
remoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
权限设置
<permission
android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" />
<uses-permission android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"/>
系统会自动生成IBookManager.java文件,文件中的大致内容
- DESCRIPTOR:Binder的唯一标识
- asInterface:将服务端的Binder对象转换成客户端所需要的AIDL接口类型对象
- 服务端与客户端在同一进程:返回服务端的Stub对象本身
- 服务端与客户端不在同一进程:但会系统封装之后的Stub.proxy
- asBinder:返回当前Binder对象
- onTransact:运行在服务端的Binder线程池中,处理客户端的请求
- 方法原型: onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)
- code:服务端用于确定客户端请求的哪一个方法
- data:目标方法所需参数
- reply:目标方法的返回值
- 方法原型: onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)
- Proxy类中的getBookList():运行在客户端,内部实现如下:
- 创建该方法所需要的输入型对象、输出型对象、返回值对象
- 将该方法的参数写入输输入型对象
- 调用transact 方法发起远程过程调用RPC,同时当前线程被挂起
- 服务端调用onTransact 方法直到RPC结束,当前线程继续执行
- 从输出型对象中取出RPC过程的结果
- 返回输出型对象中的数据