本文将介绍如何去使用AIDL,这里有2种,一种是传递标准数据类型,另一种是传递自定义数据类型。
第一种:
这里分2个项目,一个是AIDL服务端,一个是AIDL客户端。
服务端:
1.创建AIDL文件
右击你的项目,选择new->AIDL->AIDL File
然后你就能看到代码结构中多了一个AIDL文件
打开AIDL文件,你会看到这里已经有一个接口了,不用管,这个是系统自动生成的,只是告诉你可以使用哪些数据类型的。然后我们只需要添加一个我们自己的接口。
// IRemoteService.aidl
package com.example.myaidl;
// Declare any non-default types here with import statements
interface IRemoteService {
/**
* 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 getId();
}
2.创建service,把接口暴露给客户端
新建一个service,在AndroidMainfest中添加一个action,为了其他apk可以绑定这个service。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.myaidl.service.action" />
</intent-filter>
</service>
在service中实现刚才的AIDL接口
package com.example.myaidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
//实现了刚才我们定义的接口,这里就是一个简单的例子,比较简单。
@Override
public int getId() throws RemoteException {
return 100;
}
};
}
服务端的功能已经完成了,下面看如何在客户端(别的程序)如何来使用服务端(这个程序)的接口。
客户端:
1.拷贝AIDL文件
新建一个项目,直接把服务端那个AIDL文件夹拷贝到你的项目中。
2.绑定service
客户端去绑定服务端的service,客户端的onServiceConnected()回调函数将会收到一个binder,这个binder就是服务端service那里onBind()返回的,其实就是AIDL接口。
代码比较简单,点击按钮就去调用一次接口。
package com.example.myaidlclient;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.example.myaidl.IRemoteService;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private IRemoteService iRemoteService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setOnClickListener(this);
}
//这是个回调函数,在绑定service的时候要用到的
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("aaron", "Service has connected");
//返回的binder转换成你的接口
iRemoteService = IRemoteService.Stub.asInterface(service);
}
//正常unbindservice是不走这的
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("aaron", "Service has disconnected");
iRemoteService = null;
}
};
@Override
protected void onResume() {
Intent intent = new Intent();
//客户端service的报名
intent.setPackage("com.example.myaidl");
//之前service定义的action
intent.setAction("com.example.myaidl.service.action");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
@Override
public void onClick(View v) {
try {
int i = iRemoteService.getId();
Log.i("aaron", "invoke AIDL interface success " + i);
} catch (RemoteException e) {
Log.i("aaron", "exception: " + e.getMessage());
e.printStackTrace();
}
}
}
先把服务端apk装上,然后运行客户端apk
运行结果:
第二种:
描述一下,我们要实现的是传送自定义数据类型。我们这传送的是Person类的数据,里面就包含名字和年龄,比较简单。
直接在第一种方法的项目上做的,所以还留着一些第一种方法的文件。
服务端:
就前面2步比较重要,其他基本一样和上面的例子。
1.添加数据类型的AIDL
这个和上面介绍的不太一样,这个AIDL不是用来和客户端通讯的,只是为了在AIDL中能使用自定义类型的数据。
新建一个Person.aidl文件
代码如下
// Person.aidl
package com.example.myaidl;
parcelable Person;
代码很简单,就一行。
2.定义自定义数据类型
注意:这里的自定义类要和上面的那个aidl的名字,包名都要一样。
package com.example.myaidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private String age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "name: "+name+" age: "+age;
}
//下面都是自动生成的,也是必须要实现的
protected Person(Parcel in) {
name = in.readString();
age = in.readString();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(age);
}
}
下面开始的步骤基本和第一个例子一样
3.新建一个AIDL
这个AIDL接口是用来和客户端通讯的。这里就定义了2个方法,一个获取所有的Person数据,一个是根据指定名字,返回指定Person数据。
注意:这里要导入对应的Person的引用。不然会报aidl'' finished with non-zero exit value 1错误。如果Person.aidl和Person.java包名不一样也会报错。
// IPersonManager.aidl
package com.example.myaidl;
// Declare any non-default types here with import statements
import com.example.myaidl.Person;
interface IPersonManager {
/**
* 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);
List<Person> getAll();
List<Person> getByName(String name);
}
4.创建service,把接口暴露给客户端
<service
android:name=".PersonService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE"/>
</intent-filter>
</service>
这里就简单的弄了一点数据。
package com.example.myaidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
public class PersonService extends Service {
static List<Person> persons=new ArrayList<>();
static {
Person p1=new Person("bob","20");
Person p2=new Person("king","20");
Person p3=new Person("leif","30");
persons.add(p1);
persons.add(p2);
persons.add(p3);
}
public PersonService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return binder;
}
private final IPersonManager.Stub binder=new IPersonManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<com.example.myaidl.Person> getAll() throws RemoteException {
return persons;
}
@Override
public List<com.example.myaidl.Person> getByName(String name) throws RemoteException {
List<Person> list=new ArrayList<>();
for(Person p:persons){
if(p.getName().equals(name))
list.add(p);
}
return list;
}
};
}
客户端:
客户端的方法和第一个例子基本一样,只是多拷贝点文件。
1.拷贝AIDL和自定义类
这里要把Person.aidl Person.java IPersonManager.aidl都拷贝过来。
注意:包名要和服务端一样
2.绑定service
和之前一样,绑定service,然后实例化一个aidl,直接用。
界面比较简单,不上代码了。就是2个按钮,一个获取所以数据,一个根据edittext中输入的名字来获取数据。
package com.example.myaidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.example.myaidl.IPersonManager;
import com.example.myaidl.Person;
import java.util.List;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private Button button,button1;
private EditText editText;
private IPersonManager iPersonManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
button=findViewById(R.id.button2);
button.setOnClickListener(this);
button1=findViewById(R.id.button3);
button1.setOnClickListener(this);
editText=findViewById(R.id.editText);
}
private ServiceConnection mConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("aaron", "Service has connected");
iPersonManager=IPersonManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("aaron", "Service has disconnected");
iPersonManager=null;
}
};
@Override
protected void onResume() {
Intent intent=new Intent();
intent.setPackage("com.example.myaidl");
intent.setAction("android.intent.action.RESPOND_VIA_MESSAGE");
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
@Override
public void onClick(View v) {
if(v==button){
try {
List<Person> all=iPersonManager.getAll();
for (Person p:all){
Log.i("aaron","All person "+p);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}else if(v==button1){
String name=editText.getText().toString();
try {
List<Person> list=iPersonManager.getByName(name);
if(list.isEmpty()){
Log.i("aaron","no "+name);
}else {
for (Person p:list){
Log.i("aaron","person "+p);
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
运行结果:
这里也可以在调用接口的时候传递自定义数据类型,当然你还得模仿Person这样写一个aidl和一个java。比如下面,根据人来查询对应的宠物。
在定义接口的时候如果参数不是标准数据类型,要在前面加in out inout。
in表示输入型参数(Server可以获取到Client传递过去的数据,但是不能对Client端的数据进行修改) out表示输出型参数(Server获取不到Client传递过去的数据,但是能对Client端的数据进行修改) inout表示输入输出型参数(Server可以获取到Client传递过去的数据,但是能对Client端的数据进行修改)。
interface IPetManager {
Pet getPet(in Person p);
}