对于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 文件就在一个包下。
表示在跨进程通信中数据的流向(流向是针对客户端的对象而言)?
in(输入型参数):客户端——>服务器
out(输出型参数):服务器——>客户端
inout(输入输出型参数):双向流通.
????????1.?使数据类实现Parcelable接口
例:数据从客户端到服务端,可以在客户端对这个对象进行序列化(通常为实现Parcelable接口),将其中数据转化为序列化流,并传输到服务端内存中,再在服务端对这个数据进行反序列化操作,从而还原数据。
? ? ? ? 2.?建立一个类,书写其成员变量,建立getter和setter并添加一个无参构造。令这个类implements Parcelable 。
需要注意的是:默认生成的模板类对象只支持为in的定向tag。因为默认生成的类中只有writeToParcel() 方法,如果要实现为 out 或者 inout 的定向 tag 的话,需要实现readFromParcel() 方法。
新建AIDL文件,AIDL文件大致分为两类:
一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型。
// Book.aidl
//用于引入了一个序列化对象Book,供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package com.example.ipcclient;
//注意parcelable是小写
parcelable Book;
一类是用来定义方法接口,以供系统完成跨进程通信。
可理解为通信接口文件,需服务器端及客户端各一个定义,需内容相同。
// BookManager.aidl
package com.example.ipcclient;
//导入所需要使用的非默认支持数据类型的包
import com.lypeer.ipcclient.Book;
interface BookManager {
//所有的返回值前都不需要加任何东西,不管是什么数据类型
List<Book> getBooks();
Book getBook();
int getBookCount();
//传参时除了Java基本类型以及String,CharSequence之外的类型
//都需要在前面加上定向tag,具体加什么按需而定
void setBookPrice(in Book book , int price)
void setBookName(in Book book , String name)
void addBookIn(in Book book);
void addBookOut(out Book book);
void addBookInout(inout Book book);
}
?
????????IPerson.aidl
// IPerson.aidl
package com.example.aidlserver;
import com.example.aidlserver.AIDLService;
interface IPerson {
String queryPerson(int num);
}
创建完后进行build,生成IPerson.java文件
? ? ? ? ? ? ? ? i.继承Service类,定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口和IBinder接口。
? ? ? ? ? ? ? ? ii.实例化自定义的Stub类,重写onBind方法,返回binder对象。
????????AIDLService.java
package com.example.aidlserver;
public class AIDLService extends Service {
//实例化自定义的Stub类
private IBinder binder = new PersonQueryBinder();
private String[] names = {"第一个","第二个","第三个"};
private String query(int num)
{
if(num > 0 && num < 6){
return names[num - 1];
}
return null;
}
@Override
public IBinder onBind(Intent intent) {
//重写onBind方法,返回binder对象。
// return new PersonQueryBinder();
return binder;
}
//定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口。
private final class PersonQueryBinder extends IPerson.Stub {
public String queryPerson(int num) throws RemoteException {
return query(num);
}
}
}
<service android:name=".AIDLService"
android:exported="true">
<intent-filter>
<action android:name="任意名即可,如:LXYaidl" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
需要将服务端的aidl文件直接复制过来(包括其中的包),客户端的内容直接在Activity中完成即可。
流程如下:
????????????????返回的IBinder对象,只能返回onBind()方法所返回的代理对象,所需处理如下:
iPerson = IPerson.Stub.asInterface(service);
package com.example.aidlclient;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private IPerson iPerson;
private PersonConnection conn = new PersonConnection();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//控件绑定
edit_num = findViewById(R.id.edit_num);
btn_query = findViewById(R.id.btn_query);
txt_name = findViewById(R.id.txt_name);
//绑定远程Service
Intent service = new Intent("LXY_aidl");
service.setPackage("com.example.aidlserver");
//以PersonConnection对象作为参数,调用bindService绑定远程Service
bindService(service, conn, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}
@Override
public void onClick(View v) {
String number = edit_num.getText().toString();
int num = Integer.valueOf(number);
try {
txt_name.setText(iPerson.queryPerson(num));
} catch (RemoteException e) {
e.printStackTrace();
}
edit_num.setText("");
}
//自定义的PersonConnection类实现ServiceConnection接口
private final class PersonConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
iPerson = IPerson.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
iPerson = null;
}
}
}
? ? ? ? 启动步骤为:先启动服务端,再启动客户端
这个地方可能出现的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对应的完整包名,客户端应与服务端的一致。
服务端:
?客户端:
? ? ? ? 1.将writeToParcel和readFromPacel方法写入方法,将对象写入到包裹(parcel)中。
需要注意的是:写入顺序与读取顺序需要相同
????????2.在该类中添加一个名为CREATOR的static final属性 改属性需要实现:android.os.Parcelable.Creator接口
????????3.从接口中写两个方法:
? ? ? ? a.createFromParcel(Parcel source)方法:实现从source创建出JavaBean实例
????????b.newArray(int size):创建一个数组。
????????4.定向tag
2.服务端
// Person.aidl
package com.example.aidl_p;
parcelable Person;
// Salary.aidl
package com.example.aidl_p;
parcelable Salary;
package com.example.aidl_p;
public class Person implements Parcelable {
private Integer id;
private String name;
public Person(Integer id,String name){
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
protected Person(Parcel in) {
}
//写入数据到Parcel中的方法
@Override
public void writeToParcel(Parcel dest, int flags) {
//把对象所包含的数据写入到parcel中
dest.writeInt(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
//必须提供一个名为CREATOR的static final属性 该属性需要实现
//android.os.Parcelable.Creator<T>接口
public static final Creator<Person> CREATOR = new Creator<Person>() {
//从Parcel中读取数据,返回Person对象
@Override
public Person createFromParcel(Parcel in) {
return new Person(in.readInt(),in.readString());
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
//因为集合取出元素的时候是根据Person对象来取得
//需要重写hashCode()和equals()方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +((name == null)? 0 : name.hashCode());
return result;
}
//hashCode 自动生成
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) &&
Objects.equals(name, person.name);
}
}
package com.example.aidl_p;
public class Salary implements Parcelable {
private String type;
private Integer salary;
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
protected Salary(Parcel in) {
}
public Salary(String type,Integer salary){
this.type = type;
this.salary = salary;
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Salary> CREATOR = new Creator<Salary>() {
//从Parcel中读取数据,返回Person对象
@Override
public Salary createFromParcel(Parcel in) {
return new Salary(in.readString(),in.readInt());
}
@Override
public Salary[] newArray(int size) {
return new Salary[size];
}
};
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(type);
parcel.writeInt(salary);
}
public String toString(){
return "工作:" + type + " 薪水: " + salary;
}
}
// ISalary.aidl
package com.example.aidl_p;
//无论在不在同一个包中都需要导包
import com.example.aidl_p.Salary;
import com.example.aidl_p.Person;
interface ISalary {
//定义一个Person对象作为传入参数
//接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in
//获取工资信息
Salary getMsg(in Person owner);
}
<service android:name=".AidlService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
package com.example.aidl_kehu;
public class MainActivity extends AppCompatActivity {
private ISalary salaryService;
private Button btnquery;
private EditText editname;
private TextView textshow;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//返回的是代理对象,要调用这个方法
salaryService = ISalary.Stub.asInterface(iBinder); }
@Override
public void onServiceDisconnected(ComponentName componentName) {
salaryService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnquery = findViewById(R.id.btnquery);
editname = findViewById(R.id.editname);
textshow = findViewById(R.id.textshow);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.hide();
}
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.example.aidl_p");
bindService(service,connection, BIND_AUTO_CREATE);
btnquery.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View view) {
try {
String name = editname.getText().toString();
Salary salary = salaryService.getMsg(new Person(1,name));
textshow.setText(name+salary.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(connection);
}
}
? ? ? ? a.创建aidl,创建同名类,在aidl中写parcelable接口。并创建aidl实现接口方法,且需要导包。
????????b.?在类中生成set、get方法,创建对象,writeToParcel中把对象写进去,在createFromParcel中返回对象,创建toString() 方法用于输出。
? ? ? ? c.在Service中实现功能
? ? ? ? a.复制服务端的aidl和类,在MainActivity中实现功能