Android进程间通讯AIDL使用及问题详解

 之前对AIDL用的不是很多,今天抽时间对其做一下详细的了解,下面本人主要从以下几个方面对AIDL做一下总结:

1.什么是AIDL

2.为什么Android中要有AIDL

3.什么时候使用AIDL

4.具体怎么实现AIDL

下面我们就进入今天的分析,分析代码Demo会在最后附上下载地址。

一.那么首先什么是AIDL呢?

AIDL全称为Android Interface definition language,顾名思义它是一种Android内部进程通信接口的描述语言,他妈怎么这么绕啊,简单的说它就是Android进程(现在可以先知道每一个App就是一个单独的进程【一个App也可以定义不同的进程】)间通信的桥梁,通过它我们可以定义进程间的通信接口(通过它我们可以在进程间进行相互操作)。

 

二.为什么Android中要有AIDL呢?

因为Android中进程与进程之间是不能相互访问的,每一个进程只能访问自己进程内的数据及操作,每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行自己的操作,相互之间不能通信。

 

三.什么时候使用AIDL

Android官方文档介绍AIDL中有这么一句话:【Note: Using AIDL is necessary onlyif you allow clients from different applications to access your servicefor IPC and want to handle multithreading in your service. If youdo not need to perform concurrent IPC across different applications, you should create yourinterface by implementing a Binder or,if you want to perform IPC, butdo not need to handle multithreading, implement yourinterface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.】只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。而Messager是单线程处理。

 

四.怎么实现AIDL

下面AIDL我们分成两步:

1.Server端代码编写,及为我们提供服务的一端,比如我们有好几个App,有一个App中有一个超牛逼的算法,我们另外几个App中也要用这个算法,这个Server端就是提供算法的那个App

2.编写Client端代码Client端为其他几个App

 

1.下面我们看一下第一步:server端代码编写。

我们以客户端将两个int值传递给服务端,服务端进行相加后返回为例:

AS中新建一个项目,创建过程跟普通项目没有区别。然后创建我们的*.aidl文件,名称可以自己定,我这里就叫IMyAidlInterface.aidl,在我们新建项目的app文件路径上右键new->AIDL->AIDL File,如下图:


创建成功的话会在项目中新增如下目录结构:


可以看见与java同级出现了一个aidl目录,其下边的包路径与java包路径相同,再看我们新创建的.aidl文件内容,默认会有一个void basicTypes()方法,其中参数为aidl支持的几种传参基本数据类型(Aidl默认支持的类型包话java基本类型(intlongboolean等)和(StringListMapCharSequence),如果要传递自定义类型,首先要让自定义类型支持parcelable协议),然后我们可以删掉默认方法,添加我们自己的方法,如下:


然后Build->rebuild project,在之前eclipse中会自动生成.java文件,AS不行,我们必须重新编译一下项目,然后会在如下文件目录结构中出现我们梦寐以求的.java文件。


其代码如下:

[java] view plain copy
  1. /* 
  2.  * This file is auto-generated.  DO NOT MODIFY. 
  3.  * Original file: D:\\lylsoft\\android\\androidstudio\\mydemo\\AidlClient\\app\\src\\main\\aidl\\com\\jason\\aidl\\aidldemo\\IMyAidlInterface.aidl 
  4.  */  
  5. package com.jason.aidl.aidldemo;  
  6. // Declare any non-default types here with import statements  
  7. public interface IMyAidlInterface extends android.os.IInterface  
  8. {  
  9. /** Local-side IPC implementation stub class. */  
  10. public static abstract class Stub extends android.os.Binder implements com.jason.aidl.aidldemo.IMyAidlInterface  
  11. {  
  12. private static final java.lang.String DESCRIPTOR = "com.jason.aidl.aidldemo.IMyAidlInterface";  
  13. /** Construct the stub at attach it to the interface. */  
  14. public Stub()  
  15. {  
  16. this.attachInterface(this, DESCRIPTOR);  
  17. }  
  18. /** 
  19.  * Cast an IBinder object into an com.jason.aidl.aidldemo.IMyAidlInterface interface, 
  20.  * generating a proxy if needed. 
  21.  */  
  22. public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj)  
  23. {  
  24. if ((obj==null)) {  
  25. return null;  
  26. }  
  27. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  28. if (((iin!=null)&&(iin instanceof com.jason.aidl.aidldemo.IMyAidlInterface))) {  
  29. return ((com.jason.aidl.aidldemo.IMyAidlInterface)iin);  
  30. }  
  31. return new com.jason.aidl.aidldemo.IMyAidlInterface.Stub.Proxy(obj);  
  32. }  
  33. @Override public android.os.IBinder asBinder()  
  34. {  
  35. return this;  
  36. }  
  37. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  38. {  
  39. switch (code)  
  40. {  
  41. case INTERFACE_TRANSACTION:  
  42. {  
  43. reply.writeString(DESCRIPTOR);  
  44. return true;  
  45. }  
  46. case TRANSACTION_add:  
  47. {  
  48. data.enforceInterface(DESCRIPTOR);  
  49. int _arg0;  
  50. _arg0 = data.readInt();  
  51. int _arg1;  
  52. _arg1 = data.readInt();  
  53. int _result = this.add(_arg0, _arg1);  
  54. reply.writeNoException();  
  55. reply.writeInt(_result);  
  56. return true;  
  57. }  
  58. }  
  59. return super.onTransact(code, data, reply, flags);  
  60. }  
  61. private static class Proxy implements com.jason.aidl.aidldemo.IMyAidlInterface  
  62. {  
  63. private android.os.IBinder mRemote;  
  64. Proxy(android.os.IBinder remote)  
  65. {  
  66. mRemote = remote;  
  67. }  
  68. @Override public android.os.IBinder asBinder()  
  69. {  
  70. return mRemote;  
  71. }  
  72. public java.lang.String getInterfaceDescriptor()  
  73. {  
  74. return DESCRIPTOR;  
  75. }  
  76. @Override public int add(int a, int b) throws android.os.RemoteException  
  77. {  
  78. android.os.Parcel _data = android.os.Parcel.obtain();  
  79. android.os.Parcel _reply = android.os.Parcel.obtain();  
  80. int _result;  
  81. try {  
  82. _data.writeInterfaceToken(DESCRIPTOR);  
  83. _data.writeInt(a);  
  84. _data.writeInt(b);  
  85. mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
  86. _reply.readException();  
  87. _result = _reply.readInt();  
  88. }  
  89. finally {  
  90. _reply.recycle();  
  91. _data.recycle();  
  92. }  
  93. return _result;  
  94. }  
  95. }  
  96. static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
  97. }  
  98. public int add(int a, int b) throws android.os.RemoteException;  
  99. }  

可以看到,在我们编译的出的.java文件中有一个Stub内部类,我们在MainActivity同目录下创建一个Service,创建一个Stub类实现add()方法后在onBind()方法中返回,代码如下:

[java] view plain copy
  1. package com.jason.aidl.aidldemo;  
  2. import android.app.Service;  
  3. import android.content.Intent;  
  4. import android.os.IBinder;  
  5. import android.os.RemoteException;  
  6. import android.util.Log;  
  7. import com.jason.aidl.aidldemo.Person;  
  8.   
  9. public class MyAidlService extends Service {  
  10.     @Override  
  11.     public int onStartCommand(Intent intent, int flags, int startId) {  
  12.         Log.i("Log_LYL""Onstart");  
  13.         return super.onStartCommand(intent, flags, startId);  
  14.   
  15.     }  
  16.     @Override  
  17.     public IBinder onBind(Intent intent) {  
  18.         return stub;  
  19.     }  
  20.     IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {  
  21.         @Override  
  22.         public int add(int a, int b) throws RemoteException {  
  23.             return a + b;  
  24.         }  
  25.             };  
  26. }  

到这里我们的Server端代码就完成了?没有,我们要知道,我们其他项目中没有这个Service,所以我们要想在其他App中打开这个service必须通过隐式意图,所以我们必须在我们的Manifest.xml中添加一个action,如下:

[html] view plain copy
  1. <service  
  2.     android:name=".MyAidlService"  
  3.     android:enabled="true"  
  4.     android:exported="true">  
  5.     <intent-filter>  
  6.         <action android:name="com.lyl.aidl"/>  
  7.     </intent-filter>  
  8. </service>  

到这里服务端工作算是基本完成了。

2.下面看第二步编写Client端代码。

新建一个Client端项目,我们将服务端整个aidl下的文件统统考本到Client端与java同级目录下,如下图:



编译项目后会在Client端也生成一个.java文件,且与Server端生成的.java文件一模一样,然后我们就可以在我们的activity_main.xml中布局,然后在MainAcitivity中进行绑定service进行远程调用运算了,xml代码与activity代码如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/activity_main"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:orientation="vertical">  
  8.   
  9.     <TextView  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="客户端"  
  13.         android:textColor="@android:color/holo_red_dark"  
  14.         android:textSize="15sp" />  
  15.   
  16.     <EditText  
  17.         android:id="@+id/et_num1"  
  18.         android:layout_width="200dp"  
  19.         android:layout_height="wrap_content" />  
  20.   
  21.     <EditText  
  22.         android:id="@+id/et_num2"  
  23.         android:layout_width="200dp"  
  24.         android:layout_height="wrap_content" />  
  25.   
  26.     <Button  
  27.         android:layout_width="wrap_content"  
  28.         android:layout_height="wrap_content"  
  29.         android:onClick="add"  
  30.         android:text="加运算" />  
  31.   
  32.     <LinearLayout  
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="wrap_content"  
  35.         android:orientation="horizontal">  
  36.   
  37.         <TextView  
  38.             android:layout_width="0dp"  
  39.             android:layout_height="wrap_content"  
  40.             android:layout_weight="1"  
  41.             android:text="结果:" />  
  42.   
  43.         <TextView  
  44.             android:id="@+id/tv"  
  45.             android:layout_width="0dp"  
  46.             android:layout_height="wrap_content"  
  47.             android:layout_weight="2" />  
  48.     </LinearLayout>  
  49. </LinearLayout>  

[java] view plain copy
  1. package com.jason.aidl.client.aidlclient;  
  2.   
  3. import android.content.ComponentName;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.content.ServiceConnection;  
  7. import android.content.pm.PackageManager;  
  8. import android.content.pm.ResolveInfo;  
  9. import android.os.IBinder;  
  10. import android.os.RemoteException;  
  11. import android.support.v7.app.AppCompatActivity;  
  12. import android.os.Bundle;  
  13. import android.view.View;  
  14. import android.widget.EditText;  
  15. import android.widget.TextView;  
  16.   
  17. import com.jason.aidl.aidldemo.IMyAidlInterface;  
  18.   
  19. import java.util.List;  
  20.   
  21. public class MainActivity extends AppCompatActivity {  
  22.     private EditText et_num1, et_num2;  
  23.     private TextView tv;  
  24.     private IMyAidlInterface mService;  
  25.   
  26.     @Override  
  27.     protected void onCreate(Bundle savedInstanceState) {  
  28.         super.onCreate(savedInstanceState);  
  29.         setContentView(R.layout.activity_main);  
  30.         et_num1 = (EditText) findViewById(R.id.et_num1);  
  31.         et_num2 = (EditText) findViewById(R.id.et_num2);  
  32.         tv = (TextView) findViewById(R.id.tv);  
  33.         Intent intent = new Intent();  
  34.         intent.setAction("com.lyl.aidl");  
  35.   
  36.         Intent intent1 = new Intent(createExplicitFromImplicitIntent(this, intent));  
  37.         bindService(intent1, mServiceC, Context.BIND_AUTO_CREATE);  
  38.     }  
  39.   
  40.     public void add(View v) {  
  41.         int a = Integer.valueOf(et_num1.getText().toString());  
  42.         int b = Integer.valueOf(et_num2.getText().toString());  
  43.         try {  
  44.             int res = mService.add(a, b);  
  45.             tv.setText(res+"");  
  46.   
  47.         } catch (RemoteException e) {  
  48.             e.printStackTrace();  
  49.         }  
  50.     }  
  51.   
  52.     ServiceConnection mServiceC = new ServiceConnection() {  
  53.         @Override  
  54.         public void onServiceConnected(ComponentName name, IBinder service) {  
  55.             mService = IMyAidlInterface.Stub.asInterface(service);  
  56.         }  
  57.   
  58.         @Override  
  59.         public void onServiceDisconnected(ComponentName name) {  
  60.   
  61.         }  
  62.     };  
  63.   
  64.     /** 
  65.      * 兼容Android5.0中service的intent一定要显性声明 
  66.      * 
  67.      * @param context 
  68.      * @param implicitIntent 
  69.      * @return 
  70.      */  
  71.     public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {  
  72.         // Retrieve all services that can match the given intent  
  73.         PackageManager pm = context.getPackageManager();  
  74.         //通过queryIntentActivities()方法,查询Android系统的所有具备ACTION_MAIN和CATEGORY_LAUNCHER  
  75.         //的Intent的应用程序,点击后,能启动该应用,说白了就是做一个类似Home程序的简易Launcher 。  
  76.         List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);  
  77.   
  78.         // Make sure only one match was found  
  79.         if (resolveInfo == null || resolveInfo.size() != 1) {  
  80.             return null;  
  81.         }  
  82.   
  83.         // Get component info and create ComponentName  
  84.         ResolveInfo serviceInfo = resolveInfo.get(0);  
  85.         String packageName = serviceInfo.serviceInfo.packageName;  
  86.         String className = serviceInfo.serviceInfo.name;  
  87.         ComponentName component = new ComponentName(packageName, className);  
  88.   
  89.         // Create a new intent. Use the old one for extras and such reuse  
  90.         Intent explicitIntent = new Intent(implicitIntent);  
  91.   
  92.         // Set the component to be explicit  
  93.         explicitIntent.setComponent(component);  
  94.   
  95.         return explicitIntent;  
  96.     }  
  97. }  

这里用到了createExplicitFromImplicitIntent()方法是因为Android5.0serviceintent一定要显性声明否则会报如下错误:

[java] view plain copy
  1. 01-10 19:17:43.733 14662-14662/com.jason.aidl.client.aidlclient E/AndroidRuntime: FATAL EXCEPTION: main  
  2. Process: com.jason.aidl.client.aidlclient, PID: 14662  
  3. java.lang.RuntimeException: Unable to start activity ComponentInfo{com.jason.aidl.client.aidlclient/com.jason.aidl.client.aidlclient.MainActivity}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.lyl.aidl }  
  4.                                                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2381)  
  5.                                                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2443)  
  6.                                                                                       at android.app.ActivityThread.access$800(ActivityThread.java:157)  
  7.                                                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)  
  8.                                                                                       at android.os.Handler.dispatchMessage(Handler.java:102)  
  9.                                                                                       at android.os.Looper.loop(Looper.java:135)  
  10.                                                                                       at android.app.ActivityThread.main(ActivityThread.java:5344)  
  11.                                                                                       at java.lang.reflect.Method.invoke(Native Method)  
  12.                                                                                       at java.lang.reflect.Method.invoke(Method.java:372)  
  13.                                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)  
  14.                                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)  
  15.                                                                                    Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.lyl.aidl }  
  16.                                                                                       at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1781)  
  17.                                                                                       at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1880)  
  18.                                                                                       at android.app.ContextImpl.bindService(ContextImpl.java:1858)  
  19.                                                                                       at android.content.ContextWrapper.bindService(ContextWrapper.java:539)  
  20.                                                                                       at com.jason.aidl.client.aidlclient.MainActivity.onCreate(MainActivity.java:37)  
  21.                                                                                       at android.app.Activity.performCreate(Activity.java:6033)  
  22.                                                                                       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)  
  23.                                                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2334)  
  24.                                                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2443)   
  25.                                                                                       at android.app.ActivityThread.access$800(ActivityThread.java:157)   
  26.                                                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)   
  27.                                                                                       at android.os.Handler.dispatchMessage(Handler.java:102)   
  28.                                                                                       at android.os.Looper.loop(Looper.java:135)   
  29.                                                                                       at android.app.ActivityThread.main(ActivityThread.java:5344)   
  30.                                                                                       at java.lang.reflect.Method.invoke(Native Method)   
  31.                                                                                       at java.lang.reflect.Method.invoke(Method.java:372)   
  32.                                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)   
  33.                                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)   

运行我们的Server端及Client端,在Client端的两个editText中输入两个数字点击按钮运行结果如下:


看见运行结果与我们预期一样,可见我们通过AIDL实现了AppApp之间的远程调用。

 

以上只是一个简单的基本数据类型操作,那么我们怎么通过AIDL进行对象的操作呢,我们在之前代码上做些修改就行了,看下边吧。

我们在Server端与我们创建的.aidl同目录下创建一个Person.java的类与Person.aidl文件,目录结构如下


Person.java中成员变量包括一个name,一个age,ctrl+insert添加set与get方法,然后实现Parcelable接口序列化,里边有些方法需要自己实现Creator啊writeToParcel啊什么的,自己弄一下就行,两次ctrl+enter就行,然后自己写一个readFromParcel(Parcel dest)方法,要保证赋值顺序与writeToParcel中一致,否则会出问题。具体可以看下边代码:

[java] view plain copy
  1. package com.jason.aidl.aidldemo;  
  2. import android.os.Parcel;  
  3. import android.os.Parcelable;  
  4. public class Person implements Parcelable {  
  5.     private String name;  
  6.     private int age;  
  7.     public Person() {  
  8.     }  
  9.     protected Person(Parcel in) {  
  10.         name = in.readString();  
  11.         age = in.readInt();  
  12.     }  
  13.     public static final Creator<Person> CREATOR = new Creator<Person>() {  
  14.         @Override  
  15.         public Person createFromParcel(Parcel in) {  
  16.             return new Person(in);  
  17.         }  
  18.         @Override  
  19.         public Person[] newArray(int size) {  
  20.             return new Person[size];  
  21.         }  
  22.     };  
  23.     public String getName() {  
  24.         return name;  
  25.     }  
  26.     public void setName(String name) {  
  27.         this.name = name;  
  28.     }  
  29.     public int getAge() {  
  30.         return age;  
  31.     }  
  32.     public void setAge(int age) {  
  33.         this.age = age;  
  34.     }  
  35.     @Override  
  36.     public int describeContents() {  
  37.         return 0;  
  38.     }  
  39.     @Override  
  40.     public void writeToParcel(Parcel dest, int flags) {  
  41.         dest.writeString(name);  
  42.         dest.writeInt(age);  
  43.     }  
  44.     public void readFromParcel(Parcel dest) {  
  45.         name = dest.readString();  
  46.         age = dest.readInt();  
  47.     }  
  48.     @Override  
  49.     public String toString() {  
  50.         return "Person{" +  
  51.                 "name='" + name + '\'' +  
  52.                 ", age=" + age +  
  53.                 '}';  
  54.     }  
  55. }  

Person.java完成了,看一下Person.aidl文件代码,很简单,如下:

[java] view plain copy
  1. // Person.aidl.aidl  
  2. package com.jason.aidl.aidldemo;  
  3. // Declare any non-default types here with import statements  
  4. parcelable Person;  

就两行代码一个引入包,一个parcelable Person;这里注意前边的那个单词首字母是消协的,对,没错,相信自己的眼睛。


然后修改我们的IMyAidlInterface.aidl代码如下:

[java] view plain copy
  1. // IMyAidlInterface.aidl  
  2. package com.jason.aidl.aidldemo;  
  3.   
  4. // Declare any non-default types here with import statements  
  5. import com.jason.aidl.aidldemo.Person;  
  6. interface IMyAidlInterface {  
  7.             //处理基本类型;  
  8.              int add(int a,int b);  
  9.              //处理对象;  
  10.              String inPerson(in Person p);  
  11.              String outPerson(out Person p);  
  12.              String inOutPerson(inout Person p);  
  13. }  

上面的代码中你需要手动引入Person类,因为在这里系统不会帮你引入,切记切记。还有你还会发现在方法的参数类型前有in、out、inout几个东西,不懂不要后边我们再分析。

Rebuild我们的项目你会发现报如下错误:



擦,为什么呢,因为AS是gradle构建项目的,如果不配置,它默认不会从aidl文件夹下寻找资源,所以我们需要在我们app下的build.gradle中添加如下配置:

[java] view plain copy
  1. sourceSets {  
  2.     main {  
  3.         java.srcDirs = ['src/main/java''src/main/aidl']  
  4.     }  
  5. }  

之后在次rebuild就没问题了,运行后你会发现我们生成的.java文件中多了上边的几个刚刚操作对象的方法,代码就不粘了,太多了,都是系统生成的,本篇就不分析.java中的代码了,然后修改Service中的代码如下:

[java] view plain copy
  1. package com.jason.aidl.aidldemo;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.os.RemoteException;  
  7. import android.util.Log;  
  8.   
  9. import com.jason.aidl.aidldemo.Person;  
  10.   
  11. public class MyAidlService extends Service {  
  12.   
  13.   
  14.     @Override  
  15.     public int onStartCommand(Intent intent, int flags, int startId) {  
  16.         Log.i("Log_LYL""Onstart");  
  17.         return super.onStartCommand(intent, flags, startId);  
  18.   
  19.     }  
  20.   
  21.     @Override  
  22.     public IBinder onBind(Intent intent) {  
  23.         return stub;  
  24.     }  
  25.   
  26.     IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {  
  27.         @Override  
  28.         public int add(int a, int b) throws RemoteException {  
  29.             return a + b;  
  30.         }  
  31.   
  32.         @Override  
  33.         public String inPerson(Person p) throws RemoteException {  
  34.             String old = "name:" + p.getName() + " age:" + p.getAge();  
  35.             Log.d("Log_LYL:inPerson_", old);  
  36.             p.setName("李四");  
  37.             p.setAge(13);  
  38.             return "name:" + p.getName() + " age:" + p.getAge();  
  39.         }  
  40.   
  41.         @Override  
  42.         public String outPerson(Person p) throws RemoteException {  
  43.             String old = "name:" + p.getName() + " age:" + p.getAge();  
  44.             Log.d("Log_LYL:outPerson_", old);  
  45.             p.setName("周六");  
  46.             p.setAge(20);  
  47.             return "name:" + p.getName() + " age:" + p.getAge();  
  48.         }  
  49.   
  50.         @Override  
  51.         public String inOutPerson(Person p) throws RemoteException {  
  52.             String old = "name:" + p.getName() + " age:" + p.getAge();  
  53.             Log.d("Log_LYL:inOutPerson_", old);  
  54.             p.setName("弓七");  
  55.             p.setAge(57);  
  56.             return "name:" + p.getName() + " age:" + p.getAge();  
  57.         }  
  58.     };  
  59. }  

到这里Server端的代码就算是修改完成了,然后把我们添加了Person.java与Person.aidl文件的整个文件夹copy覆盖掉我们Client端之前拷贝的包重新编译,rebuild项目,报错... 没改app下的build.gradle吧,改。然后修改我们Client端的MainActivity中的textView赋值的try catch代码块,如下:

[java] view plain copy
  1. try {  
  2.     int res = mService.add(a, b);  
  3.     Person p1 = new Person();  
  4.     p1.setName("刘大");  
  5.     p1.setAge(3);  
  6.     Person p2 = new Person();  
  7.     p2.setName("赵二");  
  8.     p2.setAge(3);  
  9.     Person p3 = new Person();  
  10.     p3.setName("张三");  
  11.     p3.setAge(3);  
  12.   
  13.     tv.setText(res + "\n" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3)+"\n" + p1.toString() + "\n" + p2.toString() + "\n" + p3.toString());  
  14.   
  15. catch (RemoteException e) {  
  16.     e.printStackTrace();  
  17. }  

然后运行Server端与Client端结果如下:


再看我们Server端服务中的打印信息:


我们通过Client端的显示与Server端的打印信息分析一下上边定义的时候的那个in、out、inout的作用,再看一下我们在.aidl中定义的三个方法:String inPerson(inPerson p);String outPerson(outPerson p);String inOutPerson(inoutPerson p);

先看第一个inPerson中定义的为in,从Client端我们在textView赋值中调用inPerson传入的Person值name为“刘大”,在服务端打印中可知我们在服务端顺利接收到了,然后我们将Person的name值改为“李四”,但是我们Client端的Person本身没有改变(通过UI中Person{name=刘大age=3}可知)。

Client端调用outPerson()传入的p2对象在服务端没有接收到(通过01-10 20:24:11.038 6451-6478/com.jason.aidl.aidldemo D/Log_LYL:outPerson_: name:null age:0打印信息可知),但是服务端修改了p2我们在Client端收到了修改后的p2(通过Person{name=周六age=20}可知)。

Client端调用inoutPerson()传入p3对象在服务端可以接收到修改后在Client端也能发现P3被修改了。

由以上三种情况可以知道in的作用是Client端给Server端传递数据,Server段修改修改数据对客户端没有影响;而参数为out时,Client端给服务端传的值服务端是收不到的,服务端可以修改Client端传递过去的值,会影响到Client端队形;最后inout就是二者的结合了,Client端传递的值Server端能够收到,Server端修改了数据,会影响Client端对象数据,他们是双向操作的。

 

到此关于AIDl前边的几个问题就都回答完了,总结一下我在编写过程中遇到的几个问题:

1.Client端在绑定Server端使用隐式方式时,要兼容Android5.0以上版本。

2.传递对象实现Parcelable接口时readFromParcel(Parcel dest)中获取变量值顺序要与writeFromParcel(Parcel dest)一致。

3.传递对象时,记得配置buidle.grable,两端都要配置。

猜你喜欢

转载自blog.csdn.net/chenjie0932/article/details/79914781