服务端实现
接下来的过程演示了服务端怎么实现Android复杂的AIDL通信实例
首先需要明白的一个事情,调用一个方法,涉及到三个重要概念:参数的个数,参数类型,和方法输出类型
AIDL支持的参数类型
1. 简单数据类型
- Java基本类型,即int、long、char等;
- String;
- CharSequence;
- List
List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。 - Map
Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
2. 复杂数据类型
在AIDL中传递的复杂参数类型对象必须要实现Parcelable接口,这是因为Parcelable允许Android系统将复杂对象分解成基本类型以便在进程间传输。
若客户端组件和服务分开在不同APP,必须要把该Parcelable实现类.java文件拷贝到客户端所在的APP,包路径要一致。
另外需要为这个Parcelable实现类定义一个相应的.aidl文件。
复杂数据类型,必须要有import语句,即使它跟.aidl是同一个包下。
方法可有零、一或多个参数,可有返回值或void。
将复杂数据类型作为AIDL接口的形参时,记得加上in。
所有非基本类型的参数都需要标签来表明这个数据的去向:
in,表示此变量由客户端设置;
out,表示此变量由服务端设置;
inout,表示此变量可由客户端和服务端设置;
而基本类型只能是in。
只expose方法,不会expose静态变量。
3. 回调类参数类型
回调类型的参数类型和复杂数据类型用法是一样的,但是它对应的是binder类型的参数,而不是单纯的数据类型。回调类型的参数允许回调客户端代码及结果,相当于服务端调用客户端当中的方法,同样的 回调类参数类型也属于非基本类型参数,也需要用标签标明设置情况
需要定义一个AIDL服务需要以下几个步骤
- 创建.aidl文件
- SDK生成对应.java文件和Stub内部类
- 通过Service子类将接口暴露给外界
1. 创建.aidl文件
- 创建对象类型的AIDL文件
简单类型对象并不需要单独创建一个aidl文件表示,但是复杂对象需要创建一个对应的实体类型的aidl文件,如下:
//AIDL复杂对象定义
//注意点:这个包名需要和你的实体类com.visualing.application.RequestEntity包名一致
//同时也意味着,你需要顶一个实现Parcelable接口的实体类
package com.visualing.application;
//parcelable化的实体
parcelable RequestEntity;
该aidl文件对应的实体类实现了Parcelable接口,这是一个常规的实现了Parcelable接口的实例
package com.visualing.application;
import android.os.Parcel;
import android.os.Parcelable;
public class RequestEntity implements Parcelable {
public String name;
public long money;
public int age;
public byte sex;
protected RequestEntity(Parcel in) {
name = in.readString();
money = in.readLong();
age = in.readInt();
sex = in.readByte();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeLong(money);
dest.writeInt(age);
dest.writeByte(sex);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator CREATOR = new Creator() {
@Override
public RequestEntity createFromParcel(Parcel in) {
return new RequestEntity(in);
}
@Override
public RequestEntity[] newArray(int size) {
return new RequestEntity[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getMoney() {
return money;
}
public void setMoney(long money) {
this.money = money;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public byte getSex() {
return sex;
}
public void setSex(byte sex) {
this.sex = sex;
}
}
- 创建方法类型的AIDL文件
以下是创建一个供客户端调用的aidl文件的实例
// IMyAidlInterface.aidl
package com.visualing.application;
//导入需要别的进程访问的类对象,这些内容在客户端需要有一份拷贝,生成一模一样的内容
import com.visualing.application.RequestEntity;
import com.visualing.application.AidlCallback;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* 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);
//复杂类型演示接口
int addValues(int anInt,int bnInt,in RequestEntity request);
//计算回调类型演示接口
int progressCallback(int anInt,int bnInt,in AidlCallback callback,in RequestEntity request);
}
我们可以看到上面有一个计算回调类型演示接口
int progressCallback(int anInt,int bnInt,in AidlCallback callback,in RequestEntity request);
那其中的AidlCallback是怎么实现的呢,如下,是AidlCallback一个回调类型的方法的aidl文件的实例,和本身调用者的定义方式是一样的
- 创建回调类型的AIDL文件
// AidlCallback.aidl
package com.visualing.application;
//此处需要主动导入你写入的复杂的实体类
import com.visualing.application.RequestEntity;
// Declare any non-default types here with import statements
//回调的AIDL写法,和普通的调用方法写法一致,只是这个表示从服务端调用客户端的方法而已
interface AidlCallback {
/**
* 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
in,表示此变量由客户端设置;
out,表示此变量由服务端设置;
inout,表示此变量可由客户端和服务端设置;
基本类型只能是in。
*/
//注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
void callback(in RequestEntity entity);
}
由上,已经定义了程序所需要的所有的aidl文件。
2. SDK生成对应.java文件和Stub内部类
当编译APP时,SDK工具会将项目/src/
AidlCallback.java
package com.visualing.application;
// Declare any non-default types here with import statements
//回调的AIDL写法,和普通的调用方法写法一致,只是这个表示从服务端调用客户端的方法而已
public interface AidlCallback extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.visualing.application.AidlCallback {
private static final java.lang.String DESCRIPTOR = "com.visualing.application.AidlCallback";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.visualing.application.AidlCallback interface,
* generating a proxy if needed.
*/
public static com.visualing.application.AidlCallback asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.visualing.application.AidlCallback))) {
return ((com.visualing.application.AidlCallback) iin);
}
return new com.visualing.application.AidlCallback.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_callback: {
data.enforceInterface(DESCRIPTOR);
com.visualing.application.RequestEntity _arg0;
if ((0 != data.readInt())) {
_arg0 = com.visualing.application.RequestEntity.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.callback(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.visualing.application.AidlCallback {
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;
}
/**
* 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
* in,表示此变量由客户端设置;
* out,表示此变量由服务端设置;
* inout,表示此变量可由客户端和服务端设置;
* 基本类型只能是in。
*///注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
@Override
public void callback(com.visualing.application.RequestEntity entity) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((entity != null)) {
_data.writeInt(1);
entity.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_callback, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_callback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
* in,表示此变量由客户端设置;
* out,表示此变量由服务端设置;
* inout,表示此变量可由客户端和服务端设置;
* 基本类型只能是in。
*///注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
public void callback(com.visualing.application.RequestEntity entity) throws android.os.RemoteException;
}
IMyAidlInterface.java
public interface IMyAidlInterface extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.visualing.application.IMyAidlInterface {
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
//执行代码分类,为不同的接口方法执行不同的过程装载
switch (code) {
case INTERFACE_TRANSACTION: {
}
case TRANSACTION_basicTypes: {
}
case TRANSACTION_addValues: {
}
case TRANSACTION_progressCallback: {
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.visualing.application.IMyAidlInterface {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
}
@Override
public int addValues(int anInt, int bnInt, com.visualing.application.RequestEntity request) throws android.os.RemoteException {
}
@Override
public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request) throws android.os.RemoteException {
}
//这个与服务器对应的方法标识
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addValues = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_progressCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
//服务接口的方法定义
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int addValues(int anInt, int bnInt, com.visualing.application.RequestEntity request) throws android.os.RemoteException;
public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request) throws android.os.RemoteException;
}
从上面我们看到了一个叫做Stub内部类,里面有两个关键的方法
public boolean onTransact()
和
public static com.visualing.application.AidlCallback asInterface(android.os.IBinder obj)
作用已经在上一篇文章中说明。他们是binder的桥梁和转换,是联系两个进程的关键点。
什么是Stub内部类,使用Stub内部类需要注意的地方
Stub内部类
- .aidl文件编译后生成的.java文件中自动生成的内部类。
- public static abstract声明。
- extends android.os.Binder。
- 实现.aidl文件中定义的接口,且声明其所有方法。
实现Stub内部类要注意
- 对于传过来的调用,无法保证是在主线程中执行的。
- Service必须要考虑多线程和线程安全。
- 默认情况下,RPC都是异步的。避免在主线程中调用AIDL,不然可能会导致ANR。
- 不能给调用方回抛异常。
3. 通过Service子类将接口暴露给外界
需要创建Stub内部类的子类并实现接口的相关方法,然后通过asBinder返回给客户端使用。
package com.visualing.application.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.visualing.application.AidlCallback;
import com.visualing.application.IMyAidlInterface;
import com.visualing.application.RequestEntity;
public class ServerApplication extends Service {
IMyAidlInterface mAidlInterface = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
//方法实现
}
@Override
public int addValues(int anInt, int bnInt, RequestEntity request) throws RemoteException {
//具体方法实现
return anInt + bnInt + request.age;
}
@Override
public int progressCallback(int anInt, int bnInt, AidlCallback callback, RequestEntity request) throws RemoteException {
//具体方法实现
request.age = addValues(anInt, bnInt, request) * 10000;
callback.callback(request);
return request.age;
}
};
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//简单的类型强转
return mAidlInterface.asBinder();
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
然后在AndroidManifest.xml进行注册
那么到此服务端的AIDL就已经全部实现了。
看看服务端的文件结构图是什么样的
客户端实现
如果已经写好了服务端的AIDL,那么客户端的AIDL也就写好了,因为它们是一一对应的,需要从服务端拷贝一份AIDL文件到客户端然后同步相关代码,那么就会生成相同的类对象。
同时还需要做的事情是将AIDL中涉及到的java类文件也需要拷贝一份,因为他们是相同的处理方式,都是通过Binder进行处理的。
以下是客户端的复制文件结构图
可以看打大部分文件都是服务端文件的复制,那么客户端怎么和服务端进行沟通呢,请看关键的AidlActivity.java文件。
package com.visualing.application.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.visualing.application.AidlCallback;
import com.visualing.application.IMyAidlInterface;
import com.visualing.application.RequestEntity;
public class AidlActivity extends AppCompatActivity {
IMyAidlInterface mAidlInterface;
//关键点服务连接器
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//关键点,获取连接服务接口
/**
*
* public static com.visualing.application.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.visualing.application.IMyAidlInterface))) {
return ((com.visualing.application.IMyAidlInterface)iin);
}
return new com.visualing.application.IMyAidlInterface.Stub.Proxy(obj);
}
*
*/
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_sum_aidl);
//绑定远程服务,可能第一次无法绑上
bindRomoteService();
initActionView();
}
protected void bindRomoteService() {
final Intent intent = new Intent();
//---------关键点:确定服务的服务类型,或者加入其它权限
intent.setAction("com.visualing.application.server.MyService");
//---------关键点:确定服务所属的包名
intent.setPackage("com.visualing.application.server");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
//解绑远程服务
unbindService(mServiceConnection);
super.onDestroy();
}
protected void initActionView() {
Button buttonCalc = (Button) findViewById(R.id.buttonCalc);
buttonCalc.setOnClickListener(new View.OnClickListener() {
EditText value1 = (EditText) findViewById(R.id.value1);
EditText value2 = (EditText) findViewById(R.id.value2);
TextView result = (TextView) findViewById(R.id.result);
int v1 = 0, v2 = 0, res = -1;
@Override
public void onClick(View v) {
//远程方法操作
if (mAidlInterface != null) {
try {
v1 = Integer.parseInt(value1.getText().toString());
v2 = Integer.parseInt(value2.getText().toString());
} catch (Exception e) {
e.printStackTrace();
return;
}
//此处是则是aidl运行线程
new Thread(new Runnable() {
@Override
public void run() {
//执行远程方法
try {
//非UI运行线程
res = mAidlInterface.addValues(v1, v2, new RequestEntity(20));
try {
result.setText(Integer.valueOf(res).toString());
} catch (Exception e) {
//执行上述代码会报异常,因为在thread中执行
e.printStackTrace();
}
mAidlInterface.progressCallback(v1, v2, new AidlCallback.Stub() {
@Override
public void callback(RequestEntity entity) throws RemoteException {
try {
result.setText(Integer.valueOf(entity.age).toString());
} catch (Exception e) {
//执行上述代码会报异常,因为在thread中执行
e.printStackTrace();
}
}
}, new RequestEntity(30));
result.postDelayed(new Runnable() {
@Override
public void run() {
try {
//UI运行线程
mAidlInterface.progressCallback(v1, v2, new AidlCallback.Stub() {
@Override
public void callback(RequestEntity entity) throws RemoteException {
result.setText(Integer.valueOf(entity.age).toString());
//不会有异常,会得出最后的显示结果
}
}, new RequestEntity(50));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}, 1000);
//如果也就说明一个问题,aidl是在那个线程中调用的,那么执行的回调也是在那个线程中
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} else {
bindRomoteService();
}
}
});
}
}
执行上面的代码执行过程是这样的,首先是 bindRomoteService(),然后得到接口对象mAidlInterface ,然后调用相关的方法实现,最后需要早destory中解绑服务 unbindService(mServiceConnection);从上面的测试代码也可以看出aidl是在那个线程中调用的,那么执行的回调也是在那个线程中,aidl是允许在任何线程中调用的。
由上基本上完成了复杂模式的AIDL双向通信回调。
总结
客户端使用
ServiceConnection mServiceConnection = new ServiceConnection()
链接服务端,得到服务端定义的接口
IMyAidlInterface mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
,客户端使用mAidlInterface对象进行远程调用,该对象是一个站桩对象,通过Binder的方式调用对应的接口实例
@Override
public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request)
{
...
//mRemote虚拟机接管的远程实例对象
mRemote.transact(Stub.TRANSACTION_progressCallback, _data, _reply, 0);
...
}
,通过transact方法将信息沟通服务端,并把相关参数信息传到客户端实现。
接下来由服务端中的onBind方法中返回的Stub类型的Binder对象接收处理
public class ServerApplication extends Service {
@Override
public IBinder onBind(Intent intent) {
//简单的类型强转
return mAidlInterface.asBinder();
}
}
但是并不是直接调用Stub子类中的对应方法,而是通过Binder中的一个onTransact方法
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_progressCallback:
{
...
int _result = this.progressCallback(_arg0, _arg1, _arg2, _arg3);
...
}
...
}
处理后进行调用用户实现的方法progressCallback()
@Override
public int progressCallback(int anInt, int bnInt, AidlCallback callback, RequestEntity request) throws RemoteException {
//具体方法实现
request.age = addValues(anInt, bnInt, request) * 10000;
callback.callback(request);
return request.age;
}
的处理过程,可以看到该过程中有一个回调方法 callback.callback(request);该方法则又会通过上面的一个过程调用到客户端的callback方法,只不过这次客户端变成了服务端,服务端相当于客户端了。这样他们就实现了双向通信。
部分引用自