在Android系统中,进程间传递的数据包括Java语言支持的基本数据类型和用户自定义的数据类型,为了使数据能够穿越进程边界,所有数据都必须是“可打包”。对于Java语言的基本数据类型,打包过程是自动完成的。但对于自定义的数据类型,用户需要实现Parcelable接口,使自定义的数据类型能够转换为系统级原语保存在Parcel对象中,穿越进程边界后可再转换为初始格式。
AIDL支持的数据类型如下表:
类型 | 说明 | 需要引入 |
---|---|---|
基本数据类型 | boolean、byte、short、int、 long、char、float、double |
否 |
String | java.lang.String | 否 |
CharSequence | java.lang.CharSequence | 否 |
List | 其中元素都必须是AIDL支持的数据类型 | 否 |
Map | 其中ket和value都必须是AIDL支持的数据类型 | 否 |
其他AIDL接口 | 任何其他使用AIDL语言生成的接口类型 | 是 |
Parcelable对象 | 实现Parcelable接口的对象 | 是 |
下面以ParcelMathServiceDemo示例为参考,说明如何在远程服务中使用自定义类型。这个示例是RemoteMathServiceDemo示例的延续,查看我的RemoteMathServiceDemo示例文章 ,也定义了MathService服务,同样可以为远程调用者提供加法服务,而且同样也是没有启动界面。
不同之处在于MathService服务涉及到了自定义数据类型,在接受到输入参数后,将不再只向调用者返回long类型的数据,而是返回一个包含“加、减、乘、除”全部运算结果的对象。这个对象是一个自定义的类,为了能够使其他AIDL文件可以使用这个自定义的类,需要使用AIDL语言声明这个类。
首先建立AllResult.aidl文件
然后在AllResult.aidl文件中声明AllResult类,AllResult.aidl文件中的代码如下:
// AllResult.aidl
package com.example.remotemathservicedemo;
//在这里声明任何非默认类型
parcelable AllResult;
(这个简陋的aidl文件可不会自动生成对应的java接口文件哦)
然后在IMathService.aidl中为全运算增加新函数,返回类型就是在AllResult.aidl中定义的AllResult,代码如下:
// IMathService.aidl
package com.example.remotemathservicedemo;
// Declare any non-default types here with import statements
interface IMathService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
long Add(long a,long b);
AllResult ComputeAll(long a,long b);
}
然后Build->Make Project 重新生成接口文件,使增加的新函数生效。这时候在新生成的IMathService.java中会提示错误,因为其中需要关于AllResult的信息都找不到。
接下来手动构造AllResult类,可以放在和自动生成的IMathService.java同目录下。下面先把AllResult.java的完整代码贴出来:
package com.example.remotemathservicedemo;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by yinghao on 2016/5/7.
*/
public class AllResult implements Parcelable {
public long addResult;
public long subResult;
public long mulResult;
public double divResult;
public AllResult(long addResult, long subResult, long mulResult, double divResult) {
this.addResult = addResult;
this.subResult = subResult;
this.mulResult = mulResult;
this.divResult = divResult;
}
//从Parcel对象得到数据,拆包函数
public AllResult(Parcel parcel) {
addResult = parcel.readLong();
subResult = parcel.readLong();
mulResult = parcel.readLong();
divResult = parcel.readDouble();
}
@Override
public int describeContents() {
return 0;
}
//顾名思义 wiiteToParcel 打包函数
//将AllResult类内部的数据按照特定顺序写入Parcel对象
//写入顺序必须与构造函数读取顺序一致
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(addResult);
dest.writeLong(subResult);
dest.writeLong(mulResult);
dest.writeDouble(divResult);
}
//实现静态公共字段Creator,用来使用Parcel对象构造AllResult对象
public static final Parcelable.Creator<AllResult> CREATOR = new Creator<AllResult>() {
@Override
public AllResult createFromParcel(Parcel parcel) {
return new AllResult(parcel);
}
@Override
public AllResult[] newArray(int size) {
return new AllResult[size];
}
};
}
AllResult继承于Parcelable,其中的数据就是全运算的运算结果。
AllResult类除了基本的构造函数以外,还需要以Parcel对象为输入的构造函数,并且要重载打包函数writeToParcel()。 把这个类写完后,你就会发现IMathService.java中的错误提示消失了。
到这里,关于自定义数据类型的工作就完成了,然后在MathService.java文件中,增加用来进行全运算的ComputAll()函数,并将运算结果保存在AllResult对象中。MathService.java中的完整代码如下:
package com.example.remotemathservicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.widget.Toast;
/**
* Created by yinghao on 2016/5/7.
*/
public class MathService extends Service {
/*
1. 建立 IMathService.Stub的实例mBinder并实现AIDL文件定义的远程服务接口
2. 在onBind()方法中将mBinder返回给远程调用者
*/
private final IMathService.Stub mBinder = new IMathService.Stub(){
@Override
public long Add(long a, long b) throws RemoteException {
return a + b;
}
@Override
public AllResult ComputeAll(long a, long b) throws RemoteException {
long addResult = a + b;
long subResult = a - b;
long mulResult = a * b;
double divResult = a / b;
AllResult allResult = new AllResult(addResult, subResult, mulResult, divResult);
return allResult;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(MathService.this, "远程绑定:MathService", Toast.LENGTH_SHORT).show();
return mBinder;
}
//Return true if you would like to have the service‘s onRebind method later called when new clients bind to it.
@Override
public boolean onUnbind(Intent intent) {
Toast.makeText(MathService.this, "取消远程绑定", Toast.LENGTH_SHORT).show();
return false;
}
}
到这里,服务端的工作就全部完成了,当然如果你没看过我的上篇关于远程服务传递基本数据类型的话,那还缺少一步,注册Service。
然后就是调用者如何去绑定和调用服务,首先需要将服务端的Module中的两个aidl文件以及对应的java接口文件和自己构造的AllResult.java类全部拷贝到调用端Module中。(关于原因可在上篇文章中查看)
然后将原来的“加法运算”功能改为“全运算”功能,关于remoteMathCallerDemo中MainActivity.java中的完整代码如下:
package com.example.remotemathcallerdemo;
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.TextView;
import com.example.remotemathservicedemo.AllResult;
import com.example.remotemathservicedemo.IMathService;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private Button bind;
private Button unbind;
private Button add;
private boolean isBound = false;
private IMathService mathService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mathService = IMathService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mathService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
add = (Button) findViewById(R.id.add);
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isBound) {
final Intent serviceIntent = new Intent();
serviceIntent.setAction("com.example.remote.MathService");
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
isBound = true;
}
}
});
unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isBound) {
unbindService(mConnection);
isBound = false;
mathService = null;
}
}
});
add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mathService == null) {
textView.setText("未绑定远程服务");
return;
}
long a = Math.round(Math.random() * 100);
long b = Math.round(Math.random() * 100);
AllResult result = null;
try {
result = mathService.ComputeAll(a, b);
} catch (RemoteException e) {
e.printStackTrace();
}
String msg = "";
if (result != null) {
msg += String.valueOf(a) + "+" + String.valueOf(b) + "=" +
String.valueOf(result.addResult) + "\n";
msg += String.valueOf(a) + "-" + String.valueOf(b) + "=" +
String.valueOf(result.subResult) + "\n";
msg += String.valueOf(a) + "*" + String.valueOf(b) + "=" +
String.valueOf(result.mulResult) + "\n";
msg += String.valueOf(a) + "/" + String.valueOf(b) + "=" +
String.valueOf(result.divResult) + "\n";
}
textView.setText(msg);
}
});
}
}
运行效果如下:
对于远程服务的一些基础知识总结以及远程服务传递基本数据类型的Demo在Android Service 远程服务(点击查看),欢迎大家指出差错或交流。