Android(安卓)IPC机制(三)使用AIDL实现
16lz
2021-01-26
1、AIDL全称为:Android Interface definition language 译为:android接口定义语言
AIDL使用的数据是有限制的,可以使用的有:
基本数据类型(int、long、char、boolean、double等)
String 和CharSequence
List:只支持ArrayList,并且里面的元素也要被AIDL支持
Map:只支持HashMap,里面的Key,value都必须支持AIDL
Parcelable:所有实现了Parcelable接口的对象
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
而且自定义的Parcelable对象和AIDL对象必须要显示import进来,无论是否在同一个包里
2、在AIDL中除了基础数据类型,其他类型的参数必须标上方向:in 、out或者inout,in代表输入型参数,out代表
输出型参数,inout代表输入输出型参数
AIDL只支持方法,不支持声明静态常量
3、使用的步骤
我们需要创建aidl为后缀的文件,如下
package com.example.multiprocess.aidl;import com.example.multiprocess.aidl.Book;import com.example.multiprocess.aidl.IOnNewBookArrivedListener;interface IBookManager { List getBookList(); void addBook(in Book book);}
build之后会在生成相应的Java文件
生成的Java类本身是一个继承了android.os.IInterface(所有在Binder中传输的接口必须继承IInterface的接口)接口的类
里面有两个内部类是实现AIDL的关键,一个Stub抽象类,具体的接口方法实现在服务端;一个Proxy静态内部类,在客户端用。
/** * Local-side IPC implementation stub class.继承自Binder,但是是一个抽象类, */public static abstract class Stub extends android.os.Binder implements com.example.multiprocess.aidl.IBookManager { private static final java.lang.String DESCRIPTOR = "com.example.multiprocess.aidl.IBookManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.multiprocess.aidl.IBookManager interface, * generating a proxy if needed. */ public static com.example.multiprocess.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } //同进程时直接返回IBookManager对象,多进程则返回Proxy;服务端Stub,客户端Proxy android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.multiprocess.aidl.IBookManager))) { return ((com.example.multiprocess.aidl.IBookManager) iin); } return new com.example.multiprocess.aidl.IBookManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.example.multiprocess.aidl.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.example.multiprocess.aidl.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }/** * 一个对Binder的代理类 */private static class Proxy implements com.example.multiprocess.aidl.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.util.List getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.multiprocess.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.example.multiprocess.aidl.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book != null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }}
服务端的使用
public class BookManagerService extends Service { public final String TAG = "BookManagerService"; private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//判断服务是否被销毁 //CopyOnWriteArrayList多个线程可以同时操作同一数据,线程安全的变体 private CopyOnWriteArrayList mBooks = new CopyOnWriteArrayList<>(); //系统提供的专门用于删除跨进程listener的接口的,本身实现了线程同步 private RemoteCallbackList mListenerList = new RemoteCallbackList(); /** * 服务端实现接口方法 */ IBinder mIBinder = new IBookManager.Stub() { @Override public List getBookList() throws RemoteException { //模拟耗时操作,此方法会导致客户端ANR SystemClock.sleep(5000); return mBooks; } @Override public void addBook(Book book) throws RemoteException { if (mBooks != null) mBooks.add(book); } @Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException { mListenerList.register(listener); //准备开始调用当前注册的回调。这将创建一个回调列表的副本,你可以从使用getBroadcastItem检索条目, // 注意:一次只能激活一个广播,所以你必须确保总是从同一个线程调用这个或者是自己做同步。 // 完成后必须调用finishBroadcast。两个方法必须成对使用 final int N = mListenerList.beginBroadcast(); mListenerList.finishBroadcast(); Log.d(TAG, "registerListener, current size:" + N); } @Override public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException { boolean success = mListenerList.unregister(listener); if (success) { Log.d(TAG, "unregister success."); } else { Log.d(TAG, "not found, can not unregister."); } final int N = mListenerList.beginBroadcast(); mListenerList.finishBroadcast(); Log.d(TAG, "unregisterListener, current size:" + N); }/** *验证自定义权限和包名,此种方法服务端不会终止AIDL中的方法从而达到保护服务端的效果 */ @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { int check = checkCallingOrSelfPermission("com.example.multiprocess.ACCESS_BOOK_SERVICE"); Log.e(TAG, "permission: " + check); if (check == PackageManager.PERMISSION_DENIED) { return false; } String packageName = null; String[] packages = getPackageManager().getPackagesForUid(getCallingUid()); if (packages != null && packages.length > 0) { packageName = packages[0]; } Log.d(TAG, "onTransact: " + packageName); if (!packageName.startsWith("com.example")) { return false; } return super.onTransact(code, data, reply, flags); } }; @Override public void onCreate() { super.onCreate(); mBooks.add(new Book(1, "Android")); mBooks.add(new Book(2, "Ios")); new Thread(new ServiceWorker()).start(); } @Nullable @Override public IBinder onBind(Intent intent) {/** * 验证自定义权限,无法服务端的正常连接 */ int check = checkCallingOrSelfPermission("com.example.multiprocess.ACCESS_BOOK_SERVICE"); if (check == PackageManager.PERMISSION_DENIED) { return null; } return mIBinder; } /** * 添加新书后,遍历通知观察者 * @param book * @throws RemoteException */ private void onNewBookArrived(Book book) throws RemoteException { mBooks.add(book); final int N = mListenerList.beginBroadcast(); for (int i = 0; i < N; i++) { IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i); if (l != null) { try { l.onNewBookArrived(book); } catch (RemoteException e) { e.printStackTrace(); } } } mListenerList.finishBroadcast(); } /** * 每间隔5s添加一本书 */ private class ServiceWorker implements Runnable { @Override public void run() { // do background processing here..... while (!mIsServiceDestoryed.get()) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int bookId = mBooks.size() + 1; Book newBook = new Book(bookId, "new book#" + bookId); try { onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } } } @Override public void onDestroy() { mIsServiceDestoryed.set(true);//表示停止服务,停止添加书 super.onDestroy(); }}
客户端的调用
public class BookManagerActivity extends AppCompatActivity { private final static String TAG="BookManagerActivity"; private IBookManager mRemoteIBookManager; private static Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_NEW_BOOK_ARRIVED: Log.e(TAG,((Book)msg.obj).toString()); break; default: super.handleMessage(msg); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book_manager); Intent intent=new Intent(this,BookManagerService.class); bindService(intent,mServiceConnection,BIND_AUTO_CREATE); } private IBinder.DeathRecipient mDeathRecipient=new IBinder.DeathRecipient() { @Override public void binderDied() { Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName()); if (mRemoteIBookManager == null) return; //解除死亡通知,如果Binder死亡了,不会在触发binderDied方法 mRemoteIBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0); mRemoteIBookManager = null; // TODO:这里重新绑定远程Service } }; private ServiceConnection mServiceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager iBookManager=IBookManager.Stub.asInterface(service); mRemoteIBookManager=iBookManager; try { mRemoteIBookManager.asBinder().linkToDeath(mDeathRecipient, 0);//设置死亡代理 //服务端用到的是CopyOnWriteArrayList,但是AIDL会将其按照List接口规则将其转换为ArrayList //iBookManager.getBookList()如果为耗时操作需要开启子线程,否则会造成线程阻塞,从而ANR List books=iBookManager.getBookList(); Log.e(TAG,"list type:"+books.getClass().getCanonicalName()); Log.e(TAG,"query book list:"+books.toString()); iBookManager.registerListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) {// mRemoteIBookManager = null; Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName()); } }; private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { //此处是在Binder线程中执行的,需要用Handler跳转至主线程 handler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget(); } }; @Override protected void onDestroy() { if (mRemoteIBookManager != null && mRemoteIBookManager.asBinder().isBinderAlive()) { try { //解除绑定 mRemoteIBookManager.unregisterListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(mServiceConnection);//接触服务绑定 super.onDestroy(); }}
我们通过registerListener和unregisterListener两个方法来实现服务器对客户端的通知,相当于一个观察者模式,
当有新书的时候统一向客户端发送消息。1、还有要注意客户端和服务端方法调用时所在的线程是不同的,要根据实际
情况来进行处理。2、在解除注册监听时要注意使用RemoteCallbackList来管理监听,否则无法正确取消监听。
3、在服务端意外终止时要利用IBinder.DeathRecipient(死亡代理)或onServiceDisconnected(ComponentName name)
方法重新连接服务器。4、在界面销毁时要解除注册和解除服务绑定具体的注意在代码注释中有。
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- Android应用方法数65536的限制问题
- App应用唯一标示码
- android 不能在子线程中更新ui的讨论和分析
- Android(安卓)AsyncTask 详细解析
- 通过Intent传递一些二进制数据的方法有哪些?
- Android(安卓)异步任务使用分析
- android面试之线程、进程、Handler
- Android多线程(二)消息处理机制---Handler、Message、Looper源码原