IPC机制(一)了解AIDL
AIDL了解
AIDL这门语言的目的就是为了实现进程间通信。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求 。
aidl主要分为服务端与客户端两方面:
服务端
服务端首先创建一个Service监听客户端的连接请求,创建一个aidl文件,在service中实现,并暴露给客户端。
客户端
客户端主要绑定服务端的Service,将Binder对象转成aidl类型,就可以调用服务端的实现方法。
AIDL支持的数据类型 :
1.基本数据类型: int、long、float、double、boolean等(不包含short:aidl不支持short,因为Parcel没有办法对short进行序列化,也就没办法通过aidl将short类型在客户端与服务端进行传递)
2.String,CharSequence
3.实现了Parcelable接口的数据类型
4.List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
5.Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
AIDL文件可以分为两类。一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值
定向Tag定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。
明确导包在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下
代码较乱,kotlin和java都有…多多包涵
服务端代码
aidl接口创建
创建Aidl文件
// IMyAidlInterface.aidl
package com.example.aidl;
parcelable Student;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getName();
Student getStudent();
void setName( String name);
void setStudent(in Student stu);
void setOutName( out Student stu);
void setInOutName(inout Student stu);
void setDefaultName( String name);
}
创建Student类(需要实现Parcelable接口)
package com.example.aidl
import android.os.Parcel
import android.os.Parcelable
class Student() : Parcelable {
var name: String? = null
var old = 0
constructor(parcel: Parcel) : this() {
name = parcel.readString()
old = parcel.readInt()
}
fun readFromParcel(`in`: Parcel) {
name = `in`.readString()
old = `in`.readInt()
}
override fun toString(): String {
return "name=" + name + "-------" + "old=" + old
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeInt(old)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Student> {
override fun createFromParcel(parcel: Parcel): Student {
return Student(parcel)
}
override fun newArray(size: Int): Array<Student?> {
return arrayOfNulls(size)
}
}
}
readFromParcel()方法需要自己加进去,否则转换会报错。当然,也要有默认构造器。否则后续可能会出现错误。
rebuild工程
更改aidl文件需要重新build一下,系统自动重新生成新的IMyAidlInterface,以及内部抽象Stub类,内部类Proxy等。
创建服务端Service:
package com.example.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.example.aidl.IMyAidlInterface;
import com.example.aidl.Student;
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
if (intent != null) {
Log.e(TAG, "onBind this hash code = " + this.hashCode());
Log.e(TAG, "intent from = " + intent.getStringExtra("from"));
}
return myBinder;
}
public static MyBinder myBinder = new MyBinder();
static class MyBinder extends IMyAidlInterface.Stub {
String name;
Student student;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public Student getStudent() throws RemoteException {
if (student != null) {
Log.e(TAG, "getStudent: " + this.student.toString());
} else {
Log.e(TAG, "getStudent: ==null");
}
return student;
}
@Override
public void setStudent(Student stu) throws RemoteException {
Log.e(TAG, "studentIn: 服务端收到参数属性"+stu.toString() );
this.student = stu;
stu.setName("service in change");
Log.e(TAG, "studentIn: " + stu.hashCode());
}
@Override
public void setOutName(Student stu) throws RemoteException {
Log.e(TAG, "studentOut: 服务端收到参数属性"+stu.toString() );
student = stu;
stu.setName("service out change");
Log.e(TAG, "studentOut: " + stu.hashCode());
}
@Override
public void setInOutName(Student stu) throws RemoteException {
Log.e(TAG, "studentInOut: 服务端收到参数属性"+stu.toString() );
student = stu;
stu.setName("service InOut change");
Log.e(TAG, "studentInOut: " + stu.hashCode());
}
@Override
public void setDefaultName(String name) throws RemoteException {
this.name = name;
}
}
}
声明service 的action,由于跨进程,需要隐式启动服务。
<service android:name="com.example.service.MyService" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="com.example.service.MyService" />
</intent-filter>
</service>
启动服务
服务端启动服务或者绑定服务都可以
(最开始以为是必须的,后面好像发现不启动是不是也行?有点怪,毕竟通讯嘛,都启动吧,哈哈)。
ser = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
interfaceA = IMyAidlInterface.Stub.asInterface(service)
}
}
var intent = Intent(this, MyService::class.java);
intent.putExtra("from", "AidlTestActivity")
bindService(intent, ser, Context.BIND_AUTO_CREATE)
客户端代码
将aidl文件以及序列化类粘贴至服务端(保持包名相同)
绑定服务,连接远程服务端
ser = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAGwcz", "onServiceConnected: ");
interfaceA = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent();
intent.setAction("com.example.service.MyService");
intent.setPackage("com.example.aidlservice");
intent.putExtra("from", "MainActivity");
bindService(intent, ser, Context.BIND_AUTO_CREATE);
aidl_change1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentOut = new Student("client out Student", 1);
Log.e(TAG, "onClick:before studentOut.hashCode()=="+studentOut.hashCode() );
interfaceA.setOutName(studentOut);
Log.e(TAG, "onClick:after studentOut.hashCode()=="+studentOut.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
aidl_change2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentInOut = new Student("client inout Student", 2);
Log.e(TAG, "onClick:before studentInOut.hashCode()=="+studentInOut.hashCode() );
interfaceA.setInOutName(studentInOut);
Log.e(TAG, "onClick:after studentInOut.hashCode()=="+studentInOut.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
aidl_change3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentIn = new Student("client in Student", 3);
Log.e(TAG, "onClick:before studentIn.hashCode()=="+studentIn.hashCode() );
interfaceA.setStudent(studentIn);
Log.e(TAG, "onClick:after studentIn.hashCode()=="+studentIn.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
这样就可以双向通信了。
aidl小例子
IPC机制之了解AIDL(二)了解定向TAG