当前位置:   article > 正文

Android:AIDL简单介绍+传递简单数据+传递复杂数据

aidl

对于AIDL的学习,这些也只能说是我在学习中的理解,有理解不到位或者错的地方也欢迎指正。


1.AIDL的简单介绍

AIDL的目的就是实现进程之间的通信,尤其是在涉及多进程并发情况下的进程间通信。可以将aidl理解为两个进程之间的桥梁,并制定规则,使其传输特定数据。

1.AIDL支持的数据类型有:
  1. 基本数据类型(int、long、char、boolean、double),定向 tag 默认且只能是 in
  2. string和charSequence,定向 tag 默认且只能是 in
  3. list:只支持arraylist,以及里面的所有元素必须被aidl支持
  4. map:只支持hashmap
  5. parcelable:所有实现parcelable接口的对象
  6. aidl:所有aidl接口本身也可以在aidl文件中使用

需要注意的是:在aidl中声明其他数据类型时,需要在前面加in(输入型参数)、out(输出型参数)、inout(输入输出型参数)

需要注意的是:除aidl所支持的数据类型外,如果要使用则必须导包,就算目标文件与当前文件在同一包下。

比如:
编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.xxx.aidldemo 包下 ,现在要在 .aidl 文件里使用 Book 对象,那么就必须在 .aidl 文件里面写上 import com.xxx.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。
2.定向tag:

表示在跨进程通信中数据的流向(流向是针对客户端的对象而言) 

in(输入型参数):客户端——>服务器

out(输出型参数):服务器——>客户端

inout(输入输出型参数):双向流通.

3.如果不包含非默认支持的数据类型,只需要编写一个AIDL文件,如果包含,通常需要写 n+1 个AIDL文件( n 为非默认支持的数据类型的种类数)
4.非默认支持数据类型的序列化操作

        1. 使数据类实现Parcelable接口

例:数据从客户端到服务端,可以在客户端对这个对象进行序列化(通常为实现Parcelable接口),将其中数据转化为序列化流,并传输到服务端内存中,再在服务端对这个数据进行反序列化操作,从而还原数据。

        2. 建立一个类,书写其成员变量,建立getter和setter并添加一个无参构造。令这个类implements Parcelable 。

需要注意的是:默认生成的模板类对象只支持为in的定向tag。因为默认生成的类中只有writeToParcel() 方法,如果要实现为 out 或者 inout 的定向 tag 的话,需要实现readFromParcel() 方法。

5.书写AIDL文件 

新建AIDL文件,AIDL文件大致分为两类:

一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型。

  1. // Book.aidl
  2. //用于引入了一个序列化对象Book,供其他的AIDL文件使用
  3. //注意:Book.aidl与Book.java的包名应当是一样的
  4. package com.example.ipcclient;
  5. //注意parcelable是小写
  6. parcelable Book;

一类是用来定义方法接口,以供系统完成跨进程通信。

可理解为通信接口文件,需服务器端及客户端各一个定义,需内容相同。

  1. // BookManager.aidl
  2. package com.example.ipcclient;
  3. //导入所需要使用的非默认支持数据类型的包
  4. import com.lypeer.ipcclient.Book;
  5. interface BookManager {
  6. //所有的返回值前都不需要加任何东西,不管是什么数据类型
  7. List<Book> getBooks();
  8. Book getBook();
  9. int getBookCount();
  10. //传参时除了Java基本类型以及String,CharSequence之外的类型
  11. //都需要在前面加上定向tag,具体加什么按需而定
  12. void setBookPrice(in Book book , int price)
  13. void setBookName(in Book book , String name)
  14. void addBookIn(in Book book);
  15. void addBookOut(out Book book);
  16. void addBookInout(inout Book book);
  17. }

 

2.AIDL传递简单数据

1.服务端
        a.创建AIDL文件 

        IPerson.aidl

  1. // IPerson.aidl
  2. package com.example.aidlserver;
  3. import com.example.aidlserver.AIDLService;
  4. interface IPerson {
  5. String queryPerson(int num);
  6. }

创建完后进行build,生成IPerson.java文件

         b.自定义Service类

                i.继承Service类,定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口和IBinder接口。

                ii.实例化自定义的Stub类,重写onBind方法,返回binder对象。

        AIDLService.java

  1. package com.example.aidlserver;
  2. public class AIDLService extends Service {
  3. //实例化自定义的Stub类
  4. private IBinder binder = new PersonQueryBinder();
  5. private String[] names = {"第一个","第二个","第三个"};
  6. private String query(int num)
  7. {
  8. if(num > 0 && num < 6){
  9. return names[num - 1];
  10. }
  11. return null;
  12. }
  13. @Override
  14. public IBinder onBind(Intent intent) {
  15. //重写onBind方法,返回binder对象。
  16. // return new PersonQueryBinder();
  17. return binder;
  18. }
  19. //定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口。
  20. private final class PersonQueryBinder extends IPerson.Stub {
  21. public String queryPerson(int num) throws RemoteException {
  22. return query(num);
  23. }
  24. }
  25. }
        c.在AndroidManifest.xml文件中注册Service
  1. <service android:name=".AIDLService"
  2. android:exported="true">
  3. <intent-filter>
  4. <action android:name="任意名即可,如:LXYaidl" />
  5. <category android:name="android.intent.category.DEFAULT" />
  6. </intent-filter>
  7. </service>
2.客户端

需要将服务端的aidl文件直接复制过来(包括其中的包),客户端的内容直接在Activity中完成即可。

流程如下:

        a.自定义的PersonConnection类实现ServiceConnection接口
        b.以PersonConnection对象作为参数,调用bindService绑定远程Service
bindService(service,conn,BIND_AUTO_CREATE);
        c.绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法

                返回的IBinder对象,只能返回onBind()方法所返回的代理对象,所需处理如下:

iPerson = IPerson.Stub.asInterface(service);

代码:MainActivity.java
  1. package com.example.aidlclient;
  2. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  3. private EditText edit_num;
  4. private Button btn_query;
  5. private TextView txt_name;
  6. private IPerson iPerson;
  7. private PersonConnection conn = new PersonConnection();
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.activity_main);
  12. //控件绑定
  13. edit_num = findViewById(R.id.edit_num);
  14. btn_query = findViewById(R.id.btn_query);
  15. txt_name = findViewById(R.id.txt_name);
  16. //绑定远程Service
  17. Intent service = new Intent("LXY_aidl");
  18. service.setPackage("com.example.aidlserver");
  19. //以PersonConnection对象作为参数,调用bindService绑定远程Service
  20. bindService(service, conn, BIND_AUTO_CREATE);
  21. btn_query.setOnClickListener(this);
  22. }
  23. @Override
  24. public void onClick(View v) {
  25. String number = edit_num.getText().toString();
  26. int num = Integer.valueOf(number);
  27. try {
  28. txt_name.setText(iPerson.queryPerson(num));
  29. } catch (RemoteException e) {
  30. e.printStackTrace();
  31. }
  32. edit_num.setText("");
  33. }
  34. //自定义的PersonConnection类实现ServiceConnection接口
  35. private final class PersonConnection implements ServiceConnection {
  36. public void onServiceConnected(ComponentName name, IBinder service) {
  37. iPerson = IPerson.Stub.asInterface(service);
  38. }
  39. public void onServiceDisconnected(ComponentName name) {
  40. iPerson = null;
  41. }
  42. }
  43. }
3.启动步骤及可能出现的bug

        启动步骤为:先启动服务端,再启动客户端

这个地方可能出现的bug如下: 

  • Android AIDL客户端调用服务端方法报错空指针,即找不到那个方法。

报错为:java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.example.aidlserver.ILanguage.queryLanguage(int)' on a null object reference

 解决方法:

        1.在客户端清单文件中配置服务端service的包路径

        2.服务端的onBind方法中,返回值不可为空,返回的值为binder对象;或自定义的继承了IPerson.Stub类的类的new 方法。

  • 服务端和客户端AIDL文件所在包路径不一致,报错异常为: 

java.lang.SecurityException: Binder invocation to an incorrect interface

解决方法:修改服务端service对应的完整包名,客户端应与服务端的一致。

服务端:

 客户端:

3.AIDL传递复杂数据

1.基础操作:

        1.将writeToParcelreadFromPacel方法写入方法,将对象写入到包裹(parcel)中。

需要注意的是:写入顺序与读取顺序需要相同

        2.在该类中添加一个名为CREATORstatic final属性 改属性需要实现:android.os.Parcelable.Creator接口

        3.从接口中写两个方法:

        a.createFromParcel(Parcel source)方法:实现从source创建出JavaBean实例

        b.newArray(int size):创建一个数组。

        4.定向tag

2.服务端

代码:Step1:创建aidl文件实现接口.
1.Person.aidl
  1. // Person.aidl
  2. package com.example.aidl_p;
  3. parcelable Person;

2. Salary.aidl
  1. // Salary.aidl
  2. package com.example.aidl_p;
  3. parcelable Salary;
代码:Step2:分别建立类,需实现Parcelable接口,重写对应的方法
1.Person.java
  1. package com.example.aidl_p;
  2. public class Person implements Parcelable {
  3. private Integer id;
  4. private String name;
  5. public Person(Integer id,String name){
  6. this.id = id;
  7. this.name = name;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public Integer getId() {
  16. return id;
  17. }
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21. protected Person(Parcel in) {
  22. }
  23. //写入数据到Parcel中的方法
  24. @Override
  25. public void writeToParcel(Parcel dest, int flags) {
  26. //把对象所包含的数据写入到parcel中
  27. dest.writeInt(id);
  28. dest.writeString(name);
  29. }
  30. @Override
  31. public int describeContents() {
  32. return 0;
  33. }
  34. //必须提供一个名为CREATOR的static final属性 该属性需要实现
  35. //android.os.Parcelable.Creator<T>接口
  36. public static final Creator<Person> CREATOR = new Creator<Person>() {
  37. //从Parcel中读取数据,返回Person对象
  38. @Override
  39. public Person createFromParcel(Parcel in) {
  40. return new Person(in.readInt(),in.readString());
  41. }
  42. @Override
  43. public Person[] newArray(int size) {
  44. return new Person[size];
  45. }
  46. };
  47. //因为集合取出元素的时候是根据Person对象来取得
  48. //需要重写hashCode()和equals()方法
  49. @Override
  50. public int hashCode() {
  51. final int prime = 31;
  52. int result = 1;
  53. result = prime * result +((name == null)? 0 : name.hashCode());
  54. return result;
  55. }
  56. //hashCode 自动生成
  57. @Override
  58. public boolean equals(Object o) {
  59. if (this == o) return true;
  60. if (o == null || getClass() != o.getClass()) return false;
  61. Person person = (Person) o;
  62. return Objects.equals(id, person.id) &&
  63. Objects.equals(name, person.name);
  64. }
  65. }
2.Salary.java 
  1. package com.example.aidl_p;
  2. public class Salary implements Parcelable {
  3. private String type;
  4. private Integer salary;
  5. public Integer getSalary() {
  6. return salary;
  7. }
  8. public void setSalary(Integer salary) {
  9. this.salary = salary;
  10. }
  11. public String getType() {
  12. return type;
  13. }
  14. public void setType(String type) {
  15. this.type = type;
  16. }
  17. protected Salary(Parcel in) {
  18. }
  19. public Salary(String type,Integer salary){
  20. this.type = type;
  21. this.salary = salary;
  22. }
  23. @Override
  24. public int describeContents() {
  25. return 0;
  26. }
  27. public static final Creator<Salary> CREATOR = new Creator<Salary>() {
  28. //从Parcel中读取数据,返回Person对象
  29. @Override
  30. public Salary createFromParcel(Parcel in) {
  31. return new Salary(in.readString(),in.readInt());
  32. }
  33. @Override
  34. public Salary[] newArray(int size) {
  35. return new Salary[size];
  36. }
  37. };
  38. @Override
  39. public void writeToParcel(Parcel parcel, int i) {
  40. parcel.writeString(type);
  41. parcel.writeInt(salary);
  42. }
  43. public String toString(){
  44. return "工作:" + type + " 薪水: " + salary;
  45. }
  46. }
3.ISalary.aidl  获取工资信息的方法
  1. // ISalary.aidl
  2. package com.example.aidl_p;
  3. //无论在不在同一个包中都需要导包
  4. import com.example.aidl_p.Salary;
  5. import com.example.aidl_p.Person;
  6. interface ISalary {
  7. //定义一个Person对象作为传入参数
  8. //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in
  9. //获取工资信息
  10. Salary getMsg(in Person owner);
  11. }
4.AidlService.java 
核心Service:
定义一个SalaryBinder类继承Stub,从而实现ISalary和IBinder接口;定义一个存储信息的Map集合,
重新onBind方法,返回SalaryBinder类的对象实例
5.注册Service:
  1. <service android:name=".AidlService"
  2. android:enabled="true"
  3. android:exported="true">
  4. <intent-filter>
  5. <action android:name="android.intent.action.AIDLService" />
  6. <category android:name="android.intent.category.DEFAULT" />
  7. </intent-filter>
  8. </service>
3.客户端

代码:Step1:将服务端的aidl复制过来。
代码:Step2:定义一个ServciceConnection对象,重写对应方法,和普通数据的类似,接着bindService,然后再Button的点击事件中获取Salary对象并显示。
MainActivity.java
  1. package com.example.aidl_kehu;
  2. public class MainActivity extends AppCompatActivity {
  3. private ISalary salaryService;
  4. private Button btnquery;
  5. private EditText editname;
  6. private TextView textshow;
  7. private ServiceConnection connection = new ServiceConnection() {
  8. @Override
  9. public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
  10. //返回的是代理对象,要调用这个方法
  11. salaryService = ISalary.Stub.asInterface(iBinder); }
  12. @Override
  13. public void onServiceDisconnected(ComponentName componentName) {
  14. salaryService = null;
  15. }
  16. };
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. btnquery = findViewById(R.id.btnquery);
  22. editname = findViewById(R.id.editname);
  23. textshow = findViewById(R.id.textshow);
  24. ActionBar actionBar = getSupportActionBar();
  25. if(actionBar != null){
  26. actionBar.hide();
  27. }
  28. Intent service = new Intent("android.intent.action.AIDLService");
  29. service.setPackage("com.example.aidl_p");
  30. bindService(service,connection, BIND_AUTO_CREATE);
  31. btnquery.setOnClickListener(new View.OnClickListener() {
  32. @SuppressLint("SetTextI18n")
  33. @Override
  34. public void onClick(View view) {
  35. try {
  36. String name = editname.getText().toString();
  37. Salary salary = salaryService.getMsg(new Person(1,name));
  38. textshow.setText(name+salary.toString());
  39. } catch (RemoteException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. });
  44. }
  45. @Override
  46. protected void onDestroy() {
  47. super.onDestroy();
  48. this.unbindService(connection);
  49. }
  50. }

4.AIDL总结 

1.服务端

        a.创建aidl,创建同名类,在aidl中写parcelable接口。并创建aidl实现接口方法,且需要导包。

        b. 在类中生成set、get方法,创建对象,writeToParcel中把对象写进去,在createFromParcel中返回对象,创建toString() 方法用于输出。

        c.在Service中实现功能

2.客户端

        a.复制服务端的aidl和类,在MainActivity中实现功能

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/1001959
推荐阅读
相关标签
  

闽ICP备14008679号