IPC-AIDL的使用实例和分析

IPC-AIDL的使用实例

这份笔记主要是为了记录跨进程通信IPC的其中方法之一的AIDL 的使用.
需求假设:现在2个不同的应用需要相互直接传递消息,并且客户端还需要调用服务器的方法.这个时候使用AIDL 就是很好的选择.为了使这份笔记有更好的价值,这里客户端要求可以接到服务器的通知也就是说客户端让服务器做了一件事件,服务端做好了之后需要告诉客户端.也就是观察者模式.

当然在某些情况下使用Messenger也能解决这个需求,但是Messenger本质也是AIDL.后面也会在其他笔记里面记录如果使用Messenger来实现.
注意:  Messenger的消息传递是串行的,也就是说服务端是按照顺序一个个来来处理.不适合有大量并发请求的.这个时候用使用AIDL就合适一些.

这里为了方便,客户端就是一个Activity,服务端就是一个Service.2者在同一个app内,只是在不同的进程中.如果2个不同的应用需要实现上面的需求,只要把这里服务端的AIDL 文件拷贝到客户端的应用可以的.原理是移植的.

1.服务端

    服务端首先创建一个Service用来监听客户端发起的请求,然后创建一个AIDL文件,将暴露给客户端的的接口在这个AIDL文件里面声明,最后在Servie中实现这个AIDL接口就可以了.

2.客户端

先绑定到服务端的Service,绑定成功之后,将服务端返回的Binder对象转换为AIDL接口类型,接口就可以调用AIDL中的方法了.

3.实现代码

这里以客户端向服务端添加书籍为例

相关代码目录截图:

3.1AIDL文件


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

Book.java
   
   
  1. package com.example.aidl;
  2. import android.os.Parcel;
  3. import android.os.Parcelable;
  4. public class Book implements Parcelable{
  5. public int bookId;
  6. public String bookName ;
  7. public Book(int bookId, String bookName) {
  8. super();
  9. this.bookId = bookId;
  10. this.bookName = bookName;
  11. }
  12. public Book(Parcel in){
  13. bookId = in.readInt();
  14. bookName = in.readString();
  15. }
  16. @Override
  17. public int describeContents() {
  18. return 0;
  19. }
  20. @Override
  21. public void writeToParcel(Parcel dest, int flags) {
  22. dest.writeInt(bookId);
  23. dest.writeString(bookName);
  24. }
  25. public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
  26. @Override
  27. public Book[] newArray(int size) {
  28. return new Book[size];
  29. }
  30. @Override
  31. public Book createFromParcel(Parcel source) {
  32. return new Book(source);
  33. }
  34. };
  35. }

IBookManager.aidl

package com.example.aidl;
import com.example.aidl.Book;
import com.example.aidl.IOnNewBookListener;

interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookListener listener);
void unregistenerListener(IOnNewBookListener listener);
}

IOnNewBookListener.aidl
package com.example.aidl;
import com.example.aidl.Book;
interface IOnNewBookListener{
    void onNewBook(in Book newBook);
}  

3.2服务端代码

   
   
  1. package com.example.binder;
  2. import java.util.List;
  3. import java.util.concurrent.CopyOnWriteArrayList;
  4. import com.example.aidl.Book;
  5. import com.example.aidl.IBookManager;
  6. import com.example.aidl.IOnNewBookListener;
  7. import android.app.Service;
  8. import android.content.Intent;
  9. import android.os.IBinder;
  10. import android.os.RemoteException;
  11. import android.util.Log;
  12. public class ServerService extends Service {
  13. private static final String TAG = "ServerService";
  14. //使用CopyOnWriteArrayList是为了支持并发读写
  15. private CopyOnWriteArrayList<Book> mList = new CopyOnWriteArrayList<Book>();
  16. private CopyOnWriteArrayList<IOnNewBookListener> mListeners = new CopyOnWriteArrayList<IOnNewBookListener>();
  17. @Override
  18. public IBinder onBind(Intent intent) {
  19. return mStub;
  20. }
  21. public final IBookManager.Stub mStub = new IBookManager.Stub() {
  22. @Override
  23. public List<Book> getBookList() throws RemoteException {
  24. Log.d(TAG,"addBook-size" + mList.size());
  25. return mList;
  26. }
  27. @Override
  28. public void addBook(Book book) throws RemoteException {
  29. Log.d(TAG,"addBook name=" + book.bookName);
  30. if(!mList.contains(book)){
  31. mList.add(book);
  32. for (int i = 0; i < mListeners.size(); i++) {
  33. IOnNewBookListener listener = mListeners.get(i);
  34. listener.onNewBook(book);
  35. }
  36. }else{
  37. Log.d(TAG,"addBook already exit" );
  38. }
  39. }
  40. @Override
  41. public void registerListener(IOnNewBookListener listener)
  42. throws RemoteException {
  43. if(!mListeners.contains(listener)){
  44. Log.d(TAG,"addBook registerListener" );
  45. mListeners.add(listener);
  46. }else{
  47. Log.d(TAG,"addBook registerListener exit" );
  48. }
  49. }
  50. @Override
  51. public void unregistenerListener(IOnNewBookListener listener)
  52. throws RemoteException {
  53. if(mListeners.contains(listener)){
  54. mListeners.remove(listener);
  55. Log.d(TAG,"addBook registerListener remove" );
  56. }else{
  57. Log.d(TAG,"addBook registerListener not found,error." );
  58. }
  59. }
  60. };
  61. }
同时这里并不是在2个应用之间通讯,而且在同一个应用的2个进程之间通信,所以要把服务端的进程区别一下.关键是 android:process = ":other"就表示在进程名称为other进程里面(xxxx.xxxx:other)
AndroidManifest.xml
    
    
  1. <service android:name="com.example.binder.ServerService"
  2. android:process=":other">
  3. <intent-filter>
  4. <action android:name="android.intent.action.MAIN" />
  5. </intent-filter>
  6. </service>

3.3客户端代码:

BinderActivity.java
   
   
  1. package com.example.binder;
  2. import com.example.aidl.Book;
  3. import com.example.aidl.IBookManager;
  4. import com.example.aidl.IOnNewBookListener;
  5. import com.example.demo1.R;
  6. import android.app.Activity;
  7. import android.content.ComponentName;
  8. import android.content.Context;
  9. import android.content.Intent;
  10. import android.content.ServiceConnection;
  11. import android.os.Bundle;
  12. import android.os.IBinder;
  13. import android.os.RemoteException;
  14. import android.view.View;
  15. import android.view.View.OnClickListener;
  16. import android.widget.Button;
  17. import android.widget.TextView;
  18. public class BinderActivity extends Activity implements OnClickListener{
  19. private IBookManager mIBookManager = null;
  20. private int i = 0;
  21. private TextView tv;
  22. @Override
  23. protected void onCreate(Bundle savedInstanceState) {
  24. super.onCreate(savedInstanceState);
  25. setContentView(R.layout.activity_binder);
  26. Button send = (Button)findViewById(R.id.bt_send);
  27. send.setOnClickListener(this);
  28. tv = (TextView)findViewById(R.id.tv_msg_response);
  29. Intent intent = new Intent(this, ServerService.class);
  30. bindService(intent, sc, Context.BIND_AUTO_CREATE);
  31. }
  32. ServiceConnection sc = new ServiceConnection() {
  33. @Override
  34. public void onServiceDisconnected(ComponentName name) {
  35. mIBookManager = null;
  36. try {
  37. mIBookManager.unregistenerListener(mIOnNewBookListener);
  38. } catch (RemoteException e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. @Override
  43. public void onServiceConnected(ComponentName name, IBinder service) {
  44. mIBookManager = IBookManager.Stub.asInterface(service);
  45. try {
  46. mIBookManager.registerListener(mIOnNewBookListener);
  47. } catch (RemoteException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. };
  52. private IOnNewBookListener mIOnNewBookListener = new IOnNewBookListener.Stub() {
  53. @Override
  54. public void onNewBook(Book newBook) throws RemoteException {
  55. tv.setText("第" + i + "本书添加成功,名称是:"+ newBook.bookName);
  56. }
  57. };
  58. @Override
  59. public void onClick(View v) {
  60. try {
  61. i++;
  62. mIBookManager.addBook(new Book(1, "第" + i + "本书"));
  63. } catch (RemoteException e) {
  64. e.printStackTrace();
  65. i--;
  66. }
  67. }
  68. @Override
  69. protected void onDestroy() {
  70. try {
  71. if(mIBookManager != null && mIBookManager.asBinder().isBinderAlive()){
  72. mIBookManager.unregistenerListener(mIOnNewBookListener);
  73. }
  74. } catch (RemoteException e) {
  75. e.printStackTrace();
  76. }
  77. unbindService(sc);
  78. super.onDestroy();
  79. }
  80. }

如果客户端的 onNewBook() 方法中需要执行UI操作最后使用Handlder切换到UI线程来操作.因为 onNewBook()有可能 是执行在Binder线程池的,如果在服务端调用客户端的listener的 onNewBook 时是单独创建的一个Thread在里面执行的话,那么客户端这边执行UI操作时就会报异常.
(在打印出Thread名称的时候的,发现依然是main进程.后续再研究为什么?)

上面的客户端代码如果想取消一解注册 mIOnNewBookListener 的时候会发现解注册失败,因为找不到那个listener了.这个是因为Binder会把客户端彻底的对象重新序列化.这个时候可以使用RemoteCallBackList来解决这个问题.这个是系统专门提供用来删除跨进程listener的接口的.

这个时候需要更新服务器代码如下:
ServerService.java
   
   
  1. package com.example.binder;
  2. import java.util.List;
  3. import java.util.concurrent.CopyOnWriteArrayList;
  4. import com.example.aidl.Book;
  5. import com.example.aidl.IBookManager;
  6. import com.example.aidl.IOnNewBookListener;
  7. import android.app.Service;
  8. import android.content.Intent;
  9. import android.os.IBinder;
  10. import android.os.RemoteCallbackList;
  11. import android.os.RemoteException;
  12. import android.util.Log;
  13. public class ServerService extends Service {
  14. private static final String TAG = "ServerService";
  15. //使用CopyOnWriteArrayList是为了支持并发读写
  16. private CopyOnWriteArrayList<Book> mList = new CopyOnWriteArrayList<Book>();
  17. private RemoteCallbackList<IOnNewBookListener> mListeners = new RemoteCallbackList<IOnNewBookListener>();
  18. @Override
  19. public IBinder onBind(Intent intent) {
  20. return mStub;
  21. }
  22. public final IBookManager.Stub mStub = new IBookManager.Stub() {
  23. @Override
  24. public List<Book> getBookList() throws RemoteException {
  25. Log.d(TAG,"addBook-size" + mList.size());
  26. return mList;
  27. }
  28. @Override
  29. public void addBook(Book book) throws RemoteException {
  30. Log.d(TAG,"addBook name=" + book.bookName);
  31. if(!mList.contains(book)){
  32. mList.add(book);
  33. int N = mListeners.beginBroadcast();
  34. for (int i = 0; i < N ; i++) {
  35. IOnNewBookListener listener = mListeners.getBroadcastItem(i);
  36. try {
  37. listener.onNewBook(new Book(111, "Android xxx"));
  38. } catch (RemoteException e) {
  39. Log.d(TAG,"error");
  40. e.printStackTrace();
  41. }
  42. }
  43. mListeners.finishBroadcast();
  44. }else{
  45. Log.d(TAG,"addBook already exit" );
  46. }
  47. }
  48. @Override
  49. public void registerListener(IOnNewBookListener listener)
  50. throws RemoteException {
  51. mListeners.register(listener);
  52. }
  53. @Override
  54. public void unregistenerListener(IOnNewBookListener listener)
  55. throws RemoteException {
  56. mListeners.unregister(listener);
  57. }
  58. };
  59. }

3.4 Android.mk 与AIDL 编译问题

LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
在这句后面添加 \ 然后回车
aidl 路径 \
以下为例
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)   \
src/com/testaidl/aidl/SettingService.aidl  \
就是src 加文件-包名全路径。  注意\后要回车


4.通过AIDL来分析Binder


在这里为了加深对AIDL的理解,我们分析一下工具根据ADIL文件生成的java文件(eclipse在gen目录下面的aidl目录里面有),这里只分析 IBookManager.java 文件.
为了更清晰对这个文件结构有个认识,这里将eclipse的outline里面图保存如下


很显然,这里接口IBookManager里面有2个静态内部类.抽象类Stub和Proxy.我们已经说了 IBookManager只是接口,确实它的getBookList()等方法它自己并没有实现.
抽象类Stub和Proxy都实现了这个接口,但是 抽象类Stub并没有实现 接口IBookManager的方法.并且 抽象类Stub继承了Ibinder
但是 Proxy可不是抽象类,它也确实实现了 IBookManager接口的相关方法.但是正如它的名字他只是一个代理,是 Stub的一个内部代理.看上面的Proxy构造函数有一个Ibinder类型的参数,

下面就来分析抽象类 Stub和 Stub 的代理类 Proxy.
既然ADIL就是为了跨进程通信的,那就来看看我们实现业务逻辑的服务端,这里我们的服务端就是一个没有页面的Service,这个Service服务其实就是在内部创建了一个继承抽象类 Stub变量,并在客户端使用bindserive()连接服务端的时候,将自己创建的变量作为onBind()方法的返回参数.

在客户端使用bindService()成功连接之后,在ServiceConnection对象的onServiceConnectioned()方法的参数列表中有一个参数就是Ibinder类型.客户端想直接使用IBookManager的相关方法就要讲这个Ibinder类型的对象转换成实现了 IBookManager接口类型的对象,还是看上面截图,发现Stub有一个静态方法asInterface(xx)就是干这个的.后面客户端想干啥都可以使用这个 静态方法asInterface(xx)得到的 IBookManager对象.
注意:在这个 asInterface()内部也是会判断的,如果是同一个进程内部调用会直接获取Stub自己本身来处理(结合Stub的构造函数和 Stub的 asInterface()方法一起看就可以明白 ).如果是不同的2个进程那就创建一个 IBookManager 代理类Proxy来处理,其实就是把上面说的 Ibinder类型的对象传递给 Proxy的构造函数来处理,但是我们结合上面对服务端的分析就会发现这个客户端得到的 Ibinder类型对象与服务端的 onBind()方法的返回的IBinder肯定是有关联的.

总结起来说就是Stub类主要是负责服务端的工作,Proxy类主要是负责客户端的工作,但是客户端并不是直接创建Proxy来处理,而是使用Stub的静态方法asInterface()来将服务端返回给客户端的Ibinder变成实现了IBookManager接口的一个对象来工作的.

    
    
  1. package com.example.aidl;
  2. public interface IBookManager extends android.os.IInterface {
  3. /** Local-side IPC implementation stub class. */
  4. public static abstract class Stub extends android.os.Binder implements
  5. com.example.aidl.IBookManager {
  6. private static final java.lang.String DESCRIPTOR = "com.example.aidl.IBookManager";
  7. /** Construct the stub at attach it to the interface. */
  8. public Stub() {
  9. this.attachInterface(this, DESCRIPTOR);
  10. }
  11. /**
  12. * Cast an IBinder object into an com.example.aidl.IBookManager
  13. * interface, generating a proxy if needed.
  14. */
  15. public static com.example.aidl.IBookManager asInterface(
  16. android.os.IBinder obj) {
  17. if ((obj == null)) {
  18. return null;
  19. }
  20. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  21. if (((iin != null) && (iin instanceof com.example.aidl.IBookManager))) {
  22. return ((com.example.aidl.IBookManager) iin);
  23. }
  24. return new com.example.aidl.IBookManager.Stub.Proxy(obj);
  25. }
  26. @Override
  27. public android.os.IBinder asBinder() {
  28. return this;
  29. }
  30. @Override
  31. public boolean onTransact(int code, android.os.Parcel data,
  32. android.os.Parcel reply, int flags)
  33. throws android.os.RemoteException {
  34. switch (code) {
  35. case INTERFACE_TRANSACTION: {
  36. reply.writeString(DESCRIPTOR);
  37. return true;
  38. }
  39. case TRANSACTION_getBookList: {
  40. data.enforceInterface(DESCRIPTOR);
  41. java.util.List<com.example.aidl.Book> _result = this
  42. .getBookList();
  43. reply.writeNoException();
  44. reply.writeTypedList(_result);
  45. return true;
  46. }
  47. case TRANSACTION_addBook: {
  48. data.enforceInterface(DESCRIPTOR);
  49. com.example.aidl.Book _arg0;
  50. if ((0 != data.readInt())) {
  51. _arg0 = com.example.aidl.Book.CREATOR
  52. .createFromParcel(data);
  53. } else {
  54. _arg0 = null;
  55. }
  56. this.addBook(_arg0);
  57. reply.writeNoException();
  58. return true;
  59. }
  60. case TRANSACTION_registerListener: {
  61. data.enforceInterface(DESCRIPTOR);
  62. com.example.aidl.IOnNewBookListener _arg0;
  63. _arg0 = com.example.aidl.IOnNewBookListener.Stub
  64. .asInterface(data.readStrongBinder());
  65. this.registerListener(_arg0);
  66. reply.writeNoException();
  67. return true;
  68. }
  69. case TRANSACTION_unregistenerListener: {
  70. data.enforceInterface(DESCRIPTOR);
  71. com.example.aidl.IOnNewBookListener _arg0;
  72. _arg0 = com.example.aidl.IOnNewBookListener.Stub
  73. .asInterface(data.readStrongBinder());
  74. this.unregistenerListener(_arg0);
  75. reply.writeNoException();
  76. return true;
  77. }
  78. }
  79. return super.onTransact(code, data, reply, flags);
  80. }
  81. private static class Proxy implements com.example.aidl.IBookManager {
  82. private android.os.IBinder mRemote;
  83. Proxy(android.os.IBinder remote) {
  84. mRemote = remote;
  85. }
  86. @Override
  87. public android.os.IBinder asBinder() {
  88. return mRemote;
  89. }
  90. public java.lang.String getInterfaceDescriptor() {
  91. return DESCRIPTOR;
  92. }
  93. @Override
  94. public java.util.List<com.example.aidl.Book> getBookList()
  95. throws android.os.RemoteException {
  96. android.os.Parcel _data = android.os.Parcel.obtain();
  97. android.os.Parcel _reply = android.os.Parcel.obtain();
  98. java.util.List<com.example.aidl.Book> _result;
  99. try {
  100. _data.writeInterfaceToken(DESCRIPTOR);
  101. mRemote.transact(Stub.TRANSACTION_getBookList, _data,
  102. _reply, 0);
  103. _reply.readException();
  104. _result = _reply
  105. .createTypedArrayList(com.example.aidl.Book.CREATOR);
  106. } finally {
  107. _reply.recycle();
  108. _data.recycle();
  109. }
  110. return _result;
  111. }
  112. @Override
  113. public void addBook(com.example.aidl.Book book)
  114. throws android.os.RemoteException {
  115. android.os.Parcel _data = android.os.Parcel.obtain();
  116. android.os.Parcel _reply = android.os.Parcel.obtain();
  117. try {
  118. _data.writeInterfaceToken(DESCRIPTOR);
  119. if ((book != null)) {
  120. _data.writeInt(1);
  121. book.writeToParcel(_data, 0);
  122. } else {
  123. _data.writeInt(0);
  124. }
  125. mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
  126. _reply.readException();
  127. } finally {
  128. _reply.recycle();
  129. _data.recycle();
  130. }
  131. }
  132. @Override
  133. public void registerListener(
  134. com.example.aidl.IOnNewBookListener listener)
  135. throws android.os.RemoteException {
  136. android.os.Parcel _data = android.os.Parcel.obtain();
  137. android.os.Parcel _reply = android.os.Parcel.obtain();
  138. try {
  139. _data.writeInterfaceToken(DESCRIPTOR);
  140. _data.writeStrongBinder((((listener != null)) ? (listener
  141. .asBinder()) : (null)));
  142. mRemote.transact(Stub.TRANSACTION_registerListener, _data,
  143. _reply, 0);
  144. _reply.readException();
  145. } finally {
  146. _reply.recycle();
  147. _data.recycle();
  148. }
  149. }
  150. @Override
  151. public void unregistenerListener(
  152. com.example.aidl.IOnNewBookListener listener)
  153. throws android.os.RemoteException {
  154. android.os.Parcel _data = android.os.Parcel.obtain();
  155. android.os.Parcel _reply = android.os.Parcel.obtain();
  156. try {
  157. _data.writeInterfaceToken(DESCRIPTOR);
  158. _data.writeStrongBinder((((listener != null)) ? (listener
  159. .asBinder()) : (null)));
  160. mRemote.transact(Stub.TRANSACTION_unregistenerListener,
  161. _data, _reply, 0);
  162. _reply.readException();
  163. } finally {
  164. _reply.recycle();
  165. _data.recycle();
  166. }
  167. }
  168. }
  169. static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  170. static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  171. static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
  172. static final int TRANSACTION_unregistenerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
  173. }
  174. public java.util.List<com.example.aidl.Book> getBookList()
  175. throws android.os.RemoteException;
  176. public void addBook(com.example.aidl.Book book)
  177. throws android.os.RemoteException;
  178. public void registerListener(com.example.aidl.IOnNewBookListener listener)
  179. throws android.os.RemoteException;
  180. public void unregistenerListener(
  181. com.example.aidl.IOnNewBookListener listener)
  182. throws android.os.RemoteException;
  183. }
看了上面的eclipse的outline视图和具体的代码结构之后再看下面的UML类图对AIDl文件对应的java接口类内部结构更清晰了.下面的IxxxServie就是和我们这个实例中的IBookManager对应.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值