Android进程通信AIDL之数据监听,监听器移除
前言:抱着最起码的要求尽力去做好每一件事 ! ——秋不白
前言
Studio生成AIDL的代码编写,就不赘述了,要注意的是.aidl和对应的.java 文件的包名要保持一致。直蹦主题吧
本博客中有些细节问题忽略了,代码中也注释说明,在不考虑业务耗时的情况下,实现AIDL进程通信,数据更新监听,注册监听器和移除监听器,Binder死亡监听,服务断开连接重连解决方案。实现业务:监听图书新增变化。
支持数据类型
AIDL间通信比较简单,支持的数据类型除了基础数据类型,String CharSequence,其他定义的对象都要实现Parcelable,List只支持ArrayList,Map只支持HashMap,当然AIDL接口本身也是可以在AIDL文件中使用的。(实现AIDL中的数据更新监听)
实现功能描述
1. 监听器的添加和移除,数据监听,数据更新监听都是进程间传递数据的过程,对象传递的过程有个共同的特点,当然我们知道,对象是无法直接跨进程传递的,所以实现了Parcelable,进过序列化,反序列化后就不是同一个对象了。那如何实现移除监听器呢?这些对象传递的过程中有什么共同的特点,来实现移除对应的监听器。
答案是:这些对象传递,多次跨进程传输客户端的对象在服务端生成不同的对象,它们底层的Binder对象死同一个,利用这个特性,就可以实现移除监听器。这里就要使用到RemoteCallbackList,这不是List,内部实现了线程同步,可以简单查看源码都加上了同步锁。
2.另外还使用到的CopyOnWriteArrayList,注意它不是继承自ArrayList,同样支持并发读写。使用这个来存放Book,被监听的数据,前面提到只支持ArrayList,为什么呢,详情看AidlService中代码注释。
3. 介绍第二种,服务断开重连的解决方案。(未实践,主要是理解这种方法) 使用CountDownLatch这个类,countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次。(常用的是在onServiceDisconnected()中去处理服务重连的问题)。
文字描述结束
Activity(部分代码,客户端)
private ServiceConnection aidlConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBookManager = IBookManager.Stub.asInterface(service);
}
/**
* 服务断开重连解决方案一
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(aidlConn);
/** 移除新书到达监听*/
try {
if (iBookManager != null) {
iBookManager.unregisterListener(mOnNewBookArrivedListener);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
Log.d("RedRose", " 新书已到达");
}
};
public void openAidl(View view) {
new Thread(new Runnable() {
@Override
public void run() {
connectBinderService();
}
}).start();
}
/**
* 服务断开重连解决方案二
* 这部分代码未实践
*/
private synchronized void connectBinderService() {
/** 线程计数器*/
mConnectBinderCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent(this, AidlService.class);
bindService(intent, aidlConn, BIND_AUTO_CREATE);
try {
mConnectBinderCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
/** 对Binder 断开 重练回调*/
Log.d("RedRose", "binder died");
iBookManager.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
iBookManager = null;
connectBinderService();
}
};
/**
* 其实这样写是有问题的,因为在服务端注册和取消注册是运行在Binder线程池,如果远程调用方法
* 存在耗时,会导致主线程ANR
* 应该放到线程池去执行,这里只是侧重点是数据更新监听和取消注册
*/
public void unregisterListener(View view) {
if (iBookManager != null) {
try {
iBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void registerListener(View view) {
if (iBookManager != null) {
try {
iBookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void addBook(View view) {
try {
if (iBookManager != null) {
iBookManager.addBook(new Book());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getBookList(View view) {
try {
if (iBookManager != null) {
iBookManager.getBookList();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
Book.aidl和IBookManager.aidl
// Book.aidl
package com.redrose.aidldemo;
parcelable Book;
---------------------------------------------------------------
// IBookManager.aidl
package com.redrose.aidldemo;
// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
import com.redrose.aidldemo.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
-------------------------------------------------------------------
// IOnNewBookArrivedListener.aidl
package com.redrose.aidldemo;
// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
AidlService(服务端 为了更好体现进程通信,请在清单文件指定service的进程),可以查下RemoteCallbackList的使用方法
package com.redrose.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class AidlService extends Service {
/**
* Aidl 中使用的List只有ArrayList
* 但是CopyOnWriteArrayList 并不是继承ArrayList
* 因为AIDL中支持的是抽象的List,而List只是一个接口
* 因此服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照
* List的规范去访问数据并最终形成一个新的ArrayList传递给客户端
*/
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//使用CopyOnWriteArrayList 来存放监听器是无法实现的,进程间通信传递的不是同一个对象,最后导致无法找到,而无法正确移除
//需要使用RemoteCallbackList,因为多次跨进程传输客户端的同一个对象在服务端生成不同的对象,但是他们底层的Binder对象
//是同一个,所以就很明了了。 RemoteCallbackList内部同样也实现了线程同步的功能,可以查看源码
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBookBinder;
}
private IBinder mBookBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
onNewBookArray(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
Log.d("RedRose", " --registerListener--");
mListenerList.register(listener);
final int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d("RedRose", " mListenerList size = " + num);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
Log.d("RedRose", " --unregisterListener--");
mListenerList.unregister(listener);
final int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d("RedRose", " mListenerList size = " + num);
}
//在Binder被调用 绑定时调用,用作判断是否具有权限
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//做权限判断,没有权限就返回null
return super.onTransact(code, data, reply, flags);
}
};
private void onNewBookArray(Book book) throws RemoteException {
final int num = mListenerList.beginBroadcast();
for (int i = 0; i < num; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();
}
}
AIDL进程通信之数据监听,监听和移除总结实例就到这里了,写的比较简洁,菜鸟肯定还需要多看下AIDL方面的知识点。老司机,勿喷。