Android实现IPC进程间通信的6种方式 (五)AIDL篇

    xiaoxiao2022-07-04  137

    接前四篇 基础篇 bundle篇 文件共享篇 Messenger篇

    AIDL( Android Interface Definition Language)Android接口定义语言。上篇中的Messenger其实是一种特殊的AIDL,系统帮我们进行了封装,让我们使用起来更方便快捷。但是Messenger是串行处理消息的,不适合大量并发的情况;并且Messenger只能传递数据,客户端不能调用服务端的方法,而这些AIDL都是可以实现的 本篇我们来用AIDL实现进程间通信 服务端 1)创建我们需要传递数据类的java文件Book,实现Parcelable接口

    public class Book implements Parcelable { public String name; public Book(String name){ this.name = name; } protected Book(Parcel in) { name = 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.writeString(name); } @Override public String toString(){ return " \n Book { " + "name = " + name + "}"; } }

    2)创建传递数据的AIDL文件。 在对应的目录上,右键就可以创建AIDL文件 创建的Book.aidl

    package aidl.example.com.aidlpractise; parcelable Book;

    注意一下,创建的Book.aidl文件所在的包要与Book.java的包名相同

    3)创建传递方法的AIDL接口 创建的IMyAidlInterface.aidl

    package aidl.example.com.aidlpractise; import aidl.example.com.aidlpractise.Book; interface IMyAidlInterface { void addBook(in String name);//非基本类型数据需要标注in out inout List<Book> getBookList(); }

    4)build代码,系统自动为我们生成java文件接口 系统为我们创建的java文件目录在app --> build --> genersted --> source --> aidl --> debug --> 接口aidl包名 打开文件看一下,是这样的

    /* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\Document\\former\\Demo\\AndroidStudio_demo\\AIDLPractise\\app\\src\\main\\aidl\\aidl\\example\\com\\aidlpractise\\IMyAidlInterface.aidl */ package aidl.example.com.aidlpractise; public interface IMyAidlInterface extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements aidl.example.com.aidlpractise.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "aidl.example.com.aidlpractise.IMyAidlInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an aidl.example.com.aidlpractise.IMyAidlInterface interface, * generating a proxy if needed. */ public static aidl.example.com.aidlpractise.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof aidl.example.com.aidlpractise.IMyAidlInterface))) { return ((aidl.example.com.aidlpractise.IMyAidlInterface)iin); } return new aidl.example.com.aidlpractise.IMyAidlInterface.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_addBook: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); this.addBook(_arg0); reply.writeNoException(); return true; } case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<aidl.example.com.aidlpractise.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements aidl.example.com.aidlpractise.IMyAidlInterface { 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 void addBook(java.lang.String name) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(name); mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } //非基本类型数据需要标注in out inout @Override public java.util.List<aidl.example.com.aidlpractise.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<aidl.example.com.aidlpractise.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(aidl.example.com.aidlpractise.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void addBook(java.lang.String name) throws android.os.RemoteException; public java.util.List<aidl.example.com.aidlpractise.Book> getBookList() throws android.os.RemoteException; }

    5)服务端创建一个Service来监听客户端的连接请求,并在Service实现AIDL接口 // MyAIDLService.java

    public class MyAIDLService extends Service { private final String TAG = this.getClass().getSimpleName(); List<Book> myBookList = null; private IBinder mIBinder = new IMyAidlInterface.Stub() { @Override public void addBook(String name) throws RemoteException { myBookList.add(new Book(name)); } @Override public List<Book> getBookList() throws RemoteException { return myBookList; } }; @Nullable @Override public IBinder onBind(Intent intent) { myBookList = new ArrayList<>(); Log.d(TAG,"MyAIDLService onBind"); return mIBinder; } }

    到此,我们就把服务端的代码完成了 客户端 客户端要做的事情简单一点 1)绑定服务端的Service 2)绑定成功后,将服务端返回的Binder转换为AIDL接口所属的类型 3)在需要的地方调用AIDL接口的方法实现进程间通信 我是将客户端放在一个Activity中,点击界面的button随机产生一本书,调用addBook(),然后调用getBookList()返回所有的数目列表,显示在TextView中

    public class MainActivity extends AppCompatActivity { private IMyAidlInterface iMyAidlInterface; private LinearLayout layout; private TextView textView; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); layout = new LinearLayout(getApplicationContext()); layout.setOrientation(LinearLayout.VERTICAL); layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(layout); textView = new TextView(getApplicationContext()); textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); layout.addView(textView); button = new Button(getApplicationContext()); button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); button.setText("增加一本书"); layout.addView(button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Random random = new Random(); String name = "编程"+random.nextInt(10); try{ iMyAidlInterface.addBook(name); List<Book> lastBookList = iMyAidlInterface.getBookList(); textView.setText(lastBookList.toString()); }catch (Exception e){ e.printStackTrace(); } } }); Intent intent = new Intent(getApplicationContext(),MyAIDLService.class); bindService(intent,serviceConnection,BIND_AUTO_CREATE); } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iMyAidlInterface = null; } }; }

    Manifest文件 最后在Manifest文件中将service和activity放在不同的进程中

    <service android:name=".MyAIDLService" android:enabled="true" android:exported="true" android:process="android.aidl.service" /> <activity android:name=".MainActivity" android:process="android.aidl.client"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>

    运行结果 我们目前实现了客户端主动请求服务端,添加和查询书籍。但是实际中我们更希望服务端在有了新书的时候,主动提醒我们新书到了,这种观察者模式要怎么实现呢,下面我们来看看 首先我们需要定义一个OnNewBookArrivedListener的接口,在接口中我们将利用方法newBookArrived将新书发送给订阅者们 //OnNewBookArrivedListener

    package aidl.example.com.aidlpractise; import aidl.example.com.aidlpractise.Book; interface OnNewBookArrivedListener { void newBookArrived(in Book newBook); }

    接下来我们修改IMyAidlInterface.aidl,我们添加订阅和取消订阅接口OnNewBookArrivedListener的分两个方法 //IMyAidlInterface .aidl

    package aidl.example.com.aidlpractise; import aidl.example.com.aidlpractise.Book; import aidl.example.com.aidlpractise.OnNewBookArrivedListener; interface IMyAidlInterface { void addBook(in String name); List<Book> getBookList(); void registerListener(OnNewBookArrivedListener listener); void unRegisterListener(OnNewBookArrivedListener listener); }

    然后我们肯定要修改MyAIDLService的mIBinder,并添加一个循环来模拟新书到来,具体代码如下 //MyAIDLService

    public class MyAIDLService extends Service { private final String TAG = this.getClass().getSimpleName(); private RemoteCallbackList<OnNewBookArrivedListener> listeners;//跨进程listener接口要用RemoteCallbackList List<Book> myBookList = null; private IBinder mIBinder = new IMyAidlInterface.Stub() { @Override public void addBook(String name) throws RemoteException { myBookList.add(new Book(name)); } @Override public List<Book> getBookList() throws RemoteException { return myBookList; } @Override public void registerListener(OnNewBookArrivedListener listener) throws RemoteException { listeners.register(listener); Log.d(TAG,"registerListener"); } @Override public void unRegisterListener(OnNewBookArrivedListener listener) throws RemoteException { listeners.unregister(listener); } }; @Nullable @Override public IBinder onBind(Intent intent) { myBookList = new ArrayList<>(); listeners = new RemoteCallbackList<>(); Log.d(TAG,"MyAIDLService onBind"); refreshNewBook(); return mIBinder; } private Timer timer = new Timer(); private TimerTask timerTask = new TimerTask() { @Override public void run() { try { int N = listeners.beginBroadcast(); for(int i=0; i< N; i++){//RemoteCallbackList比需采用这种方式遍历 OnNewBookArrivedListener listener = listeners.getBroadcastItem(i); listener.newBookArrived(new Book("新书" + new Random().nextInt(10))); Log.d(TAG, "new book arrived"); } }catch (RemoteException e) { e.printStackTrace(); }finally { listeners.finishBroadcast(); } } }; private void refreshNewBook() { timer.schedule(timerTask,100,2000); } }

    接下来就是在客户端添加OnNewBookArrivedListener的监听了 //MainActivity

    public class MainActivity extends AppCompatActivity{ private IMyAidlInterface iMyAidlInterface; private LinearLayout layout; private TextView textView; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); layout = new LinearLayout(getApplicationContext()); layout.setOrientation(LinearLayout.VERTICAL); layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(layout); textView = new TextView(getApplicationContext()); textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); layout.addView(textView); button = new Button(getApplicationContext()); button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); button.setText("增加一本书"); layout.addView(button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Random random = new Random(); String name = "编程"+random.nextInt(10); try{ iMyAidlInterface.addBook(name);//客户端访问服务端方法,客户端会挂起,尽量放在非主线程中 List<Book> lastBookList = iMyAidlInterface.getBookList();//客户端访问服务端方法,耗时,尽量放在非主线程中 textView.setText(lastBookList.toString()); }catch (Exception e){ e.printStackTrace(); } } }); Intent intent = new Intent(getApplicationContext(),MyAIDLService.class); bindService(intent,serviceConnection,BIND_AUTO_CREATE); } private OnNewBookArrivedListener onNewBookArrivedListener = new OnNewBookArrivedListener.Stub() { @Override public void newBookArrived(Book newBook) throws RemoteException { if(textView != null) { iMyAidlInterface.addBook(newBook.name); List<Book> lastBookList = iMyAidlInterface.getBookList(); Message message = new Message(); message.obj= lastBookList.toString(); message.what = 0; handler.sendMessage(message); } } }; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg){ switch (msg.what){ case 0: textView.setText(msg.obj.toString()); } } }; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service); try { iMyAidlInterface.registerListener(onNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { iMyAidlInterface = null; } }; @Override public void onDestroy(){ try { iMyAidlInterface.unRegisterListener(onNewBookArrivedListener); unbindService(serviceConnection); } catch (RemoteException e) { e.printStackTrace(); } super.onDestroy(); } }

    接下来我们运行代码

    到这里我们就把AIDL的基础知识学习完了

    下一篇 ContentProvider实现进程间通信

    特别感谢《Android 开发艺术探索》

    最新回复(0)