本文主要梳理Service中远程服务相关内容,重点学习adil相关用法,若对Servcie的基础不扎实,建议先去阅读Servcie全面解析——本地服务全面解析
AIDL简述
AIDL的定义是 Android Interface Definition Language,即Android接口定义语言。没错,AIDL是一门语言,那么它就包含一系列的语法定义以及它的各类用法。
Android为何要设计这门语言?
下面是官方文档中对于AIDL的介绍
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger.
注意:只有当你允许来自不同应用程序的客户端访问你的IPC服务并希望处理服务中的多线程时,才需要使用AIDL。 如果你不需要在不同的应用程序间执行并发IPC,你应该通过实现一个Binder来创建你的接口,或者,如果你想执行IPC,但不需要处理多线程,可以使用Messenger来实现你的接口。
可见AIDL适用于Android中的进程间通信,并且需要执行并发IPC时的场景。而Messenger在Android中进行进程间通信,是同步进行的,无法处理并发情况。
AIDL语法
AIDL中支持的类型有如下几种:
AIDL编写流程
AIDL的实现分以下步骤:
AIDL具体实现
public class NBAStar implements Parcelable {
private String name;
private int age;
private String team;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getTeam() {
return team;
}
public void setTeam(String team) {
this.team = team;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(team);
}
public static final Creator CREATOR = new Creator() {
@Override
public NBAStar createFromParcel(Parcel source) {
NBAStar NBAStar = new NBAStar();
NBAStar.setName(source.readString());
NBAStar.setAge(source.readInt());
NBAStar.setTeam(source.readString());
return NBAStar;
}
@Override
public NBAStar[] newArray(int size) {
return new NBAStar[size];
}
};
@Override
public String toString() {
return name + "," + age + "," + team + ";";
}
}
创建好的目录如上图所示,其中IAidlInterfase.aidl文件是AIDL接口文件,用于客户端的调用
// IAidlInterface.aidl
package review.heal.com.review;
// Declare any non-default types here with import statements
import review.heal.com.review.NBAStar;
interface IAidlInterface {
List getAllStars();
void addStar(in NBAStar NBAStar);
}
实现Parcelable接口的NBAStar类需要一个实体映射的.aidl文件NBAStar.aidl,在NBAStar.aidl中需要声明实体类和类型
// IAidlInterface.aidl
package review.heal.com.review;
// Declare any non-default types here with import statements
parcelable NBAStar;
注意:NBAStar.aidl的包名要和实体类NBAStar的包名保持一致
在创建完以上文件之后,在Android Studio中选择Build->Make Project,此时将会出现一个错误
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Unable to find source java class:
'/Users/Shared/app/AndroidApp/remoteService/app/src/main/aidl/review/heal/com/review/NBAStar.java'
出现这个错误的原因,是因为在Android Studio中使用Gradle来构建Android项目,而Gradle会默认使用sourceSet来配置不同文件的访问路径,Gradle默认将Java文件访问路径设置在Java包下,若Java文件被放置在aidl包下,那么Android Studio将无法找到此java文件。问题的解决方法实在build.gradle中添加下面语句
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
意思是将Java代码的访问路径设置成Java包和aidl包,这样便能在aidl包中找到java文件了。
此时再在Android Studio中选择Build->Make Project,Android Studio就会帮我们生成IAidlInterface文件。
以下代码由系统自动生成,这些代码用来设定基于IAidlInterface接口的Binder IPC连接,以便客户端与远程服务通信。
public interface IAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements review.heal.com.review.IAidlInterface {
private static final java.lang.String DESCRIPTOR = "review.heal.com.review.IAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an review.heal.com.review.IAidlInterface interface,
* generating a proxy if needed.
*/
public static review.heal.com.review.IAidlInterface asInterface(android.os.IBinder obj) {
......
return new review.heal.com.review.IAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
......
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements review.heal.com.review.IAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//所有的返回值前都不需要加任何东西,不管是什么数据类型
@Override
public java.util.List getAllStars() throws android.os.RemoteException {
......
return _result;
}
@Override
public void addStar(review.heal.com.review.NBAStar NBAStar) throws android.os.RemoteException {
......
}
}
static final int TRANSACTION_getAllStars = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStar = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
//所有的返回值前都不需要加任何东西,不管是什么数据类型
public java.util.List getAllStars() throws android.os.RemoteException;
public void addStar(review.heal.com.review.NBAStar NBAStar) throws android.os.RemoteException;
}
public class NBAStarAidlService extends Service {
private List nbaStars;
private IBinder iBinder = new IAidlInterface.Stub() {
//返回所有NBAStar
@Override
public List getAllStars() throws RemoteException {
return nbaStars;
}
//增加NBAStar
@Override
public void addStar(NBAStar NBAStar) throws RemoteException {
nbaStars.add(NBAStar);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
nbaStars = new ArrayList<>();
NBAStar nbaStar = new NBAStar();
nbaStar.setName("科比");
nbaStar.setAge(30);
nbaStar.setTeam("洛杉矶湖人");
nbaStars.add(nbaStar);
return iBinder;
}
}
同时要在AndroidMainfest文件中声明服务
服务端主要做的事,是实现了 IAidlInterface的Stub接口,并在onBind()方法中返回IBinder,客户端获取IBinder对象进行交互。
首先需要将服务端的IAidlInterfase.aidl、NBAStar.aidl以及NBAStar实体类移植到客户端上,要保证包名跟服务端的一致,同样在Android Studio中选择Build->Make Project,生成IAidlInterface文件。
然后实例化ServiceConnection,获取服务端IBinder对象
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iAidlInterface = IAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
通过IAidlInterface的Stub抽象类调用asInterface()方法,可以将IBinder对象转换成IAidlInterface的实例,调用bindService()方法绑定远程服务
bindService(new Intent(IAidlInterface.class.getName()), serviceConnection, Context.BIND_AUTO_CREATE);
在Android5.0之后,上面的绑定调用会提示 Service Intent must be explicit 错误,也就是说,在Android5.0之后,服务必须显式的开启。以下方法是一个隐式调用转换为显式调用
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
PackageManager pm = context.getPackageManager();
List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Intent explicitIntent = new Intent(implicitIntent);
explicitIntent.setComponent(component);
return explicitIntent;
}
bindService(new Intent(createExplicitFromImplicitIntent(MainActivity.this, new Intent(IAidlInterface.class.getName()))), serviceConnection, Context.BIND_AUTO_CREATE)
这样客户端便完成了远程绑定服务端,调用IAidlInterface的getAllStars()方法
try {
if (iAidlInterface != null) {
Log.e("aidl", "接收aidl数据:" + iAidlInterface.getAllStars());
}
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
调用结果:
review.heal.com.remoteservice E/aidl: 接收aidl数据:[科比,30,洛杉矶湖人;]
调用IAidlInterface的addStar()方法后再调用getAllStars()方法
try {
if (iAidlInterface != null) {
NBAStar nbaStar = new NBAStar();
nbaStar.setName("麦迪");
nbaStar.setAge(31);
nbaStar.setTeam("休斯顿火箭");
iAidlInterface.addStar(nbaStar);
}
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
调用结果:
review.heal.com.remoteservice E/aidl: 接收aidl数据:[科比,30,洛杉矶湖人;, 麦迪,31,休斯顿火箭;]
以上就是AIDL实现跨进程通信的全过程