借鉴自开发艺术
Messenger就是消息队列形式的,不用考虑并发,只会一个个处理,所以不太适合并发的情况。而且Messenger无法实现跨进程调用服务端的方法。所以需要使用AIDL。AIDL是Messenger的底层实现。
服务端
需要先创建一个Service来监听客户端的连接请求。然后创建一个AIDL文件,将暴露给客户端的端口在这个AIDL文件中声明。最后在Service中实现这个AIDL接口即可。
客户端
绑定服务端的Service,绑定成功后,将服务器返回的Binder对象转换成转成AIDL接口所属的类型,接着去调用AIDL里的方法。
AIDL接口的创建(实体类不要放到AIDL文件夹下)
Book类(不在AIDL文件夹下)
public class Book implements Parcelable{ public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } }
AIDL文件夹下
// Book.aidl package com.example.test; parcelable Book;
// IBookManager.aidl package com.example.test; import com.example.test.Book; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
再make project会自动生成IBookManager的java文件
AIDL支持的数据类型:
基本数据类型
String、CharSequence
ArrayList,其中的元素必须被AIDL支持
HashMap,其中的元素(key,value)必须被AIDL支持
实现了Parcelable的对象
AIDL
如果是实现了Parcelable的对象,必须创建一个同名的AIDL文件,并且用parcelable指定。
AIDL除了基本类型,其他类型的参数必须标上方向:in out inout(两者皆可)
AIDL接口中只支持方法,不支持声明静态变量
(AIDL自动生成会报错java类会报错,可以看:http://blog.csdn.net/qq_36523667/article/details/79187244)
远程服务端Service的实现
public class BookManagerService extends Service { //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步 private CopyOnWriteArrayList<Book> mBookList = 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); } }; public BookManagerService() { } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1, "Android")); mBookList.add(new Book(2, "IOS")); } @Override public IBinder onBind(Intent intent) { return mBinder; } }
客户端
public class BookManagerActivity extends AppCompatActivity { private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager bookManager = IBookManager.Stub.asInterface(service);//得到了服务端的调用者 List<Book> list = null; try { list = bookManager.getBookList(); } catch (RemoteException e) { e.printStackTrace(); } Log.i("xbh", list.getClass().getCanonicalName()); Log.i("xbh", list.toString()); } @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() { unbindService(mConnection); super.onDestroy(); } }
观察者模式
IBookManager需要添加注册和取消注册的方法
interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unRegisterListener(IOnNewBookArrivedListener listener); }
增加一个AIDL接口
// IOnNewBookArrivedListener.aidl package com.example.test.aidl; // Declare any non-default types here with import statements import com.example.test.aidl.Book; interface IOnNewBookArrivedListener { void onNewBookArrived(in Book newBook); }
服务端修改后的代码
public class BookManagerService extends Service { private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false); //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步 private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); 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); } @Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException { if (!mListenerList.contains(listener)) { mListenerList.add(listener); } else { Log.i("xbh", "already exists"); } Log.i("xbh", "registerListener, size" + mListenerList.size()); } @Override public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException { if (mListenerList.contains(listener)) { mListenerList.remove(listener); Log.i("xbh", "unregister succeed"); } else { Log.i("xbh", "not fount, can not register"); } Log.i("xbh", "unRegisterListener, current size:" + mListenerList.size()); } }; public BookManagerService() { } @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 IBinder onBind(Intent intent) { return mBinder; } @Override public void onDestroy() { mIsServiceDestoryed.set(true); super.onDestroy(); } private void onNewBookArrived(Book book) throws RemoteException { mBookList.add(book); Log.i("xbh", "onNewBookArrived, notify listeners:" + mListenerList.size()); //观察者模式:在更新后,以调用回调形式,全部提醒 for (int i = 0; i < mListenerList.size(); i ++) { IOnNewBookArrivedListener listener = mListenerList.get(i); Log.i("xbh", "onNewBookArrived, notify listeners:" + listener); listener.onNewBookArrived(book);//异进程回调,通过AIDL } } private class ServiceWorker implements Runnable { @Override public void run() { while (!mIsServiceDestoryed.get()) { //如果还没有被销毁,睡眠5秒,生成一本新书 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(); } } } } }
客户端
public class BookManagerActivity extends AppCompatActivity { private IBookManager mRemoteBookManager; //接收到服务器返回消息的处理 private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 666: Log.i("xbh", "receive new book" + msg.obj); break; } return false; } }); private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager bookManager = IBookManager.Stub.asInterface(service);//获得服务端的“调用权” mRemoteBookManager = bookManager; List<Book> list; try { list = bookManager.getBookList(); Log.i("xbh", "query book list, list type:" + list.getClass().getCanonicalName()); Log.i("xbh", "query book list" + list.toString()); //这里的add 和观察者无关 Book newBook = new Book(3, "Android进阶"); bookManager.addBook(newBook); Log.i("xbh", "add book:" + newBook); List<Book> newList = bookManager.getBookList(); Log.i("xbh", "query book list" + newList.toString()); bookManager.registerListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mRemoteBookManager = null; Log.i("xbh", "binder died"); } }; private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { mHandler.obtainMessage(666, newBook).sendToTarget();//参数来自Service } }; @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() { if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) { Log.i("xbh", "unregister listener" + mOnNewBookArrivedListener); try { mRemoteBookManager.unRegisterListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(mConnection); super.onDestroy(); } }
mHandler.obtainMessage(666, newBook).sendToTarget();这句发消息的代码,很精炼,创建了一个msg,键为666,值为object,并且handler被复制到message的target中,再最后一句代码内部实现是target.send
但是解除订阅就行不通了,因为他的原理是在服务端创建一个新的完全一样的listener(对象实质上都是反序列化的过程),所以对象已经不一样了,地址也不一样了,所以在onDestory中的销毁自然是无效的。可以通过RemoteCallbackList。它的内部有一个Map存储所有的AIDL回调。key是IBinder类型,value是Callback类型。
IBinder key = listener.asBinder();
Callback value = new Callback(listener, cookie);
虽然传输的过程中会生成多个对象,但是这些对象的共同点是他们底层的Binder对象是同一个。当客户端解注册的时候,只需要遍历服务端所有的listener,找到Binder一样的listener并删除即可。
RemoteCallbackList做的另一件事就是当客户端进程终止的时候,它可以自动解除客户端注册的listener。
此外,它内部实现了线程同步。
使用RemoteCallbackList后的服务端代码如下所示
public class BookManagerService extends Service { private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false); //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步 private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); //重写自动生成类中的方法 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); } @Override public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException { mListenerList.unregister(listener); } }; public BookManagerService() { } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1, "Android"));//这里其实就是native操作 mBookList.add(new Book(2, "IOS")); new Thread(new ServiceWorker()).start(); } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onDestroy() { mIsServiceDestoryed.set(true); super.onDestroy(); } 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.getBroadcastItem(i); if (l != null) { l.onNewBookArrived(book); } } mListenerList.finishBroadcast(); } private class ServiceWorker implements Runnable { @Override public void run() { while (!mIsServiceDestoryed.get()) { //如果还没有被销毁,睡眠5秒,生成一本新书 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(); } } } } }
注意,当服务端执行耗时操作的时候,他的被调用的方法是运行在Binder线程池中的,我们无需为他异步;我们客户端调用了服务端的方法后,客户端会挂起,所以我们不期待在客户端的UI线程里执行服务端的耗时操作。而且客户端的onServiceConnected和onServiceDisconected也是运行在UI线程中的。怎么规避呢?开个子线程即可。
同理,当远程服务端调用客户端的listener的方法时,这个方法是运行在客户端的Binder线程池中,而服务端同样不期待调用耗时操作(服务也是运行在主线程中的)。
此外,上面的服务端调用客户端的listener方法,这个方法是运行在Binder线程池中的,所以不可以操作UI,需要用Handler切换到UI线程,代码中已经给出了这个切换方法。
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { mHandler.obtainMessage(666, newBook).sendToTarget();//参数来自Service;这里的发消息代码,很厉害,高度理解 } };
private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 666: Log.i("xbh", "receive new book" + msg.obj); break; } return false; } });之所以可以切换,是因为Handler中的Loop消息队列,隐式是主线程的。(PS:如果想获取子线程的消息队列,给某个子线程发消息,就要在这个子线程中,获取looper并给Handler即可。类似于这样mHandler = new Handler(thread.looper))
为了程序的健壮性,需要考虑Binder的意外死亡。
1.给Binder设置DeathRecipient监听,当Binder死亡,我们会收到binderDied的回调,在binderDied中可以重连远程服务
2.onServiceDisconnected中重连远程服务。区别在于前者是跑在Binder线程池的某个线程中,后者是在UI线程中。根据不同的线程,你可以做一些不同的事。
如何在AIDL中使用权限验证功能,不让任何人都可以连接。
1.onBind中验证,验证不通过返回null,验证失败的客户端无法绑定服务,用permission来验证
<permission
android:name="com.example.test.permission.ACCESS_BOOK_SERVICE"
android:protextionLevel="normal"/>
(需要明白permission的定义方式)
验证
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.example.test.permission.ACCESS_BOOK_SERVICE");
if(check == PackageManager.PERMISSIION_DENIED) {
return null;
}
return mBinder;
}
一个应用来绑定我们的服务时,会验证这个应用的权限,如果没有这个权限,onBind就会返回null。这个方法同样适用于Messenger。
如果是自己的内部应用想绑定到服务中,只需要
<user-permission android:name="com.example.test.permission.ACCESS_BOOK_SERVICE"/>
2.在服务端的onTransact方法中进行权限验证。
验证方式有permission,Uid,Pid,
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int check = checkCallingOrSelfPermission("com.example.test.permission.ACCESS_BOOK_SERVICE");
if(check == PackageManager.PERMISSION_DENIED) {
return false;
}
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if(packages != NULL && PACKAGES.length > 0) [
packageName = packages[0];
}
if(!packageName.startsWith("com.example")) {
return false;
}
return super,onTransact(code, data, reply, flags);
}