前言
AIDL,Android Interface Definition Language,Android接口定义语言,可以使当前的应用绑定一个其他进程的Service,调用远程服务实现跨进程通信。
一、基本使用
首先我们要说明的是AIDL可以实现进程间通讯,我们则需要有两个APP进程,分为服务端和客户端,服务端设置被调用的Service,而客户端则是去调用服务端的Service中的内容,在此以两个APP为例进行说明。
1. 服务端AIDL文件创建
我们首先建立一个APP工程作为服务端,右键新建一个AIDL文件:
创建一个文件:IMyAidlInterface.aidl,代码如下:
// IMyAidlInterface.aidl
package com.example.workdemo2;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getName();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
其中basicTypes()
是默认方法,可以直接空实现,getName()
是我们自定义的方法,创建完成之后,我们需要执行一下build。
支持的基本数据类型
默认情况下AIDL支持如下几种基本数据类型:
- Java的基本数据类型:int,long,boolean,double等。
- String字符串
- 自定义的Parcelable对象
- List:List中类型需要是上述的数据类型或者是Parcelable类型
- Map:Map中类型需要是上述的数据类型或者是Parcelable类型
2. 服务端Service创建
由于服务端是需要提供Service给客户端调用,所以需要创建一个Service,我们新建立一个Service——MyService,在Service中创建一个继承自刚才AIDL Stub的内部类MyBinder,实现其接口方法,在onBind中返回内部类实例,代码如下:
public class MyService extends Service {
public MyService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setForegroundService();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends IMyAidlInterface.Stub{
@Override
public String getName() throws RemoteException {
return "测试数据";
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
/**
* 将service设置为前台服务
*/
private void setForegroundService() {
String channelId = "测试Service";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = manager.getNotificationChannel(channelId);
if (notificationChannel == null) {
notificationChannel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setVibrationPattern(new long[0]);
notificationChannel.setSound(null, null);
manager.createNotificationChannel(notificationChannel);
}
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId);
builder.setSound(null);
builder.setVibrate(new long[]{});
startForeground(101, builder.build());
}
}
在AndroidManifest.xml
文件声明时,我们把Service声明为其他进程可以调用:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.RESPOND_AIDL_MESSAGE1"></action>
</intent-filter>
</service>
3. 客户端AIDL同步
执行上述步骤之后,服务端的代码就基本完成了,下面我们首先把刚才在服务端的AIDL文件copy到客户端中,然后执行build。
需要注意的是客户端和服务端的AIDL文件的包名需要完全一致。
4. 客户端调用
同步了客户端的AIDL之后,下一步是需要将当前的呃服务绑定到客户端组件上,以绑定到Activity中为例,代码如下,基本上绑定Service的逻辑:
/**
* 绑定AIDL Service
*/
private void bindAIDL() {
Intent intent = new Intent();
intent.setAction("android.intent.action.RESPOND_AIDL_MESSAGE1");
Intent finalIntent = achieveExplicitFromImplicitIntent(this, intent);
// intent.setPackage("com.example.workdemo2");
Logger.d("设置Intent");
bindService(finalIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
Logger.d("连接断开");
}
}, BIND_AUTO_CREATE);
}
/**
* 设置AIDL intent
*
* @param context
* @param implicitIntent
* @return
*/
public Intent achieveExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
在Activity中调用bindAIDL()
方法,之后就可以调用getName()方法了。
二、自定义数据类型
在实际开发中,只使用AIDL的默认类型是不够的,有时候需要使用自定义的类型,则需要将自定义类型继承Parcelable接口。
1. 自定义数据类型
我们新建一个People类,实现Parcelable接口,代码如下:
import android.os.Parcel;
import android.os.Parcelable;
public class People implements Parcelable {
int age;
String Name;
public People(int age, String name) {
this.age = age;
this.Name = name;
}
protected People(Parcel in) {
age = in.readInt();
Name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(Name);
}
public static final Creator<People> CREATOR = new Creator<People>() {
@Override
public People createFromParcel(Parcel in) {
return new People(in);
}
@Override
public People[] newArray(int size) {
return new People[size];
}
};
@Override
public int describeContents() {
return 0;
}
}
2. AIDL文件添加
在完成了上述定义后,我们就可以将People对象添加到AIDL文件中,添加parcelable People;
就可以是AIDL支持People数据对象,如下:
// IMyAidlInterface.aidl
package com.example.workdemo2;
//添加自定义的parcelable对象
parcelable People;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getName();
People getPeople();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
响应的Service中的MyBinder类也需要同步接口变化:
class MyBinder extends IMyAidlInterface.Stub{
@Override
public String getName() throws RemoteException {
return "测试数据";
}
@Override
public People getPeople() throws RemoteException {
return new People(10,"jim");
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
3. AIDL同步
将AIDL文件和自定义类型Java文件同步copy到客户端代码中,调用方法和上述的使用基本相同,就不再赘述了。
需要注意的是AIDL文件和自定义类型的文件都必须要和服务端的包名保持一致。
总结
AIDL的基本使用