AIDL Service用于在不同进程间进行数据交换,即所谓的跨进程通信(interprocess communication,简称IPC)
与绑定本地Service不同的是,本地Service的onBind()方法会直接把IBinder对象本身传给客户端的ServiceConnection的onServiceConnected()方法的第二个参数。但远程Service的onBind()方法只是将IBinder对象的代理传给客户端的ServiceConnection的onServiceConnected()方法的第二个参数
1、创建AIDL接口文件
AIDL的语法与Java接口很相似,但是有几点要求:
(1) AIDL定义的接口文件必须以.aidl结尾
(2) AIDL接口中用到的数据类型,除了基本类型、String、List、Map、CharSequence之外,其他类型都需要导入包,即使在同一个包中也需要导入
新建客户端工程,创建IBook.aidl文件:
package com.example.testaidlservice;
interface IBook {
String getName();
int getPageCount();
}
定义好IBook.aidl接口文件后,ADT工具会自动在gen目录下生成一个IBook.java文件,在java文件里包含一个Stub内部类,该内部类实现了IBinder、IBook两个接口,因此可作为Service的onBind()方法的返回值,这个Stub类将会作为远程Service的回调类
2、定义远程Service类
新建类BookService.java继承Service类
public class BookService extends Service {
private BookBinder bookBinder;
private String bookName = "Android开发艺术探索";
private int pageCount = 524;
public class BookBinder extends Stub {
@Override
public String getName() throws RemoteException {
// TODO Auto-generated method stub
return bookName;
}
@Override
public int getPageCount() throws RemoteException {
// TODO Auto-generated method stub
return pageCount;
}
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
bookBinder = new BookBinder();
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return bookBinder;
}
}
3、在AndroidManifest.xml中配置该Service
<service android:name="com.example.testaidlservice.BookService">
<intent-filter >
<action android:name="com.example.testaidlservice.BOOK_SERVICE"/>
intent-filter>
service>
配置好以后,就可以在客户端调用远程Service了,Service端目录结构:
4、客户端访问AIDL Service
AIDL接口定义了两个进程之间的通信接口,所以客户端也需要用到AIDL接口。所以在创建客户端后,要将Service端的AIDL接口文件复制到客户端,复制后ADT工具会生成相应的接口文件
客户端绑定到远程Service需要两步:
(1)创建ServiceConnection对象
(2)调用Context的bindService()方法绑定远程Service
客户端界面布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.testaidlservice.MainActivity" >
<Button
android:id="@+id/bind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="绑定远程Service"/>
<Button
android:id="@+id/get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取Service数据"/>
<TextView
android:id="@+id/bookName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/pageCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
LinearLayout>
客户端代码:
public class MainActivity extends ActionBarActivity implements OnClickListener {
IBook iBook;
Button bind, get;
TextView bookName, pageCount;
ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
//获取远程Service的代理对象
iBook = IBook.Stub.asInterface(service);
};
public void onServiceDisconnected(ComponentName name) {
iBook = null;
};
};
public void bind() {
Intent intent = new Intent(this, BookService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
Toast.makeText(this, "绑定Service成功", Toast.LENGTH_LONG).show();
}
public void getData() throws RemoteException {
//通过获得的代理对象访问Service中的数据
String name = iBook.getName();
int count = iBook.getPageCount();
bookName.setText("书名:" + name);
pageCount.setText("页数:" + count);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind = (Button)findViewById(R.id.bind);
get = (Button)findViewById(R.id.get);
bookName = (TextView)findViewById(R.id.bookName);
pageCount = (TextView)findViewById(R.id.pageCount);
bind.setOnClickListener(this);
get.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case R.id.bind:
bind();
break;
case R.id.get:
try {
getData();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
客户端目录结构:
绑定远程Service后,在ServiceConnection的onServiceConnected()方法中可以获取远程Service的代理对象,通过返回的代理对象就可以访问远程Service中的数据
运行客户端程序,点击绑定远程Service,效果如下图所示:
点击获取Service数据:
传递自定义类型的AIDL Service
当传递的数据中包含自定义类型时,需要对自定义的类型进行处理,Android要求调用远程Service中的自定义类型都必须实现Parcelable接口。实现Parcelable接口时,不仅要实现接口的方法,而且在实现类中必须定义一个类型为Parcelable.Create类型、名称为CREATOR的静态常量,除此之外,所有用到的自定义类型必须用AIDL进行定义。
在本例中需要使用到Person和Book两个自定义类型,首先使用AIDL定义,新建Person.aidl文件,代码如下:
parcelable Person;
然后定义实现Parcelable接口的Person类,直接上代码:
public class Person implements Parcelable {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
//将对象的信息写入到Parce中
dest.writeInt(id);
dest.writeString(name);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Person createFromParcel(Parcel source) {
//从Parcel中读取数据,返回Person对象
return new Person(source.readInt(), source.readString());
};
public Person[] newArray(int size) {
return new Person[size];
};
};
}
定义好Person类以后,用同样的方法定义Book类,有了这两个类,就可以来定义AIDL通信接口了,新建IBook.aidl文件,代码如下:
package com.example.testaidlservice;
import com.example.testaidlservice.Person;
import com.example.testaidlservice.Book;
interface IBook {
List getBooks(in Person owner);
}
AIDL接口在定义方法时,需要指定形参的传递模式,分为输入参数和输出参数,Java中一般都是采用传入参数的形式,所以指定Person类型为in模式
Book类和Person类跟IBook文件在同一个目录下,但也需要手动导入
接下来定义Service类
public class BookService extends Service {
private BookBinder bookBinder = new BookBinder();
private static Map> data = new HashMap>();
private static List list1 = new ArrayList();
private static List list2 = new ArrayList();
private static Person jack = new Person(1, "Jack");
private static Person jet = new Person(2, "Jet");
static {
list1.add(new Book("疯狂Android讲义", 704));
list1.add(new Book("Android开发艺术探索", 524));
data.put(jack, list1);
list2.add(new Book("疯狂Java讲义", 887));
list2.add(new Book("Android经典项目案例开发实战宝典", 638));
data.put(jet, list2);
}
public class BookBinder extends Stub {
@Override
public List getBooks(Person owner) throws RemoteException {
// TODO Auto-generated method stub
String name = owner.getName();
if(name.equals("Jack")) {
return data.get(jack);
}else{
return data.get(jet);
}
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return bookBinder;
}
}
在代码中使用静态初始化块向一个map集合中添加了2条数据,每条数据的key为Person类型,值为List类型,List中包含Person类所拥有的Book信息
在AndroidManifest.xml中配置Service:
<service android:name="com.example.testaidlservice.BookService">
<intent-filter >
<action android:name="com.example.testaidlservice.BOOK_SERVICE"/>
intent-filter>
service>
这样,Service端所需要的类都已定义完毕,目录结构:
接下来创建客户端,首先将Service端的AIDL通信接口文件拷贝到客户端,包括用到的Person.aidl、Book.aidl、Person.java、Book.java和IBook.aidl全部复制到客户端中,这样会在客户端的gen目录下生成IBook.java文件。
最后,在客户端中访问Service中的数据,客户端代码
布局文件:
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.testaidlservice.MainActivity" >
MainActivity代码:
public class MainActivity extends ActionBarActivity implements OnClickListener {
IBook iBook;
Button bind, getJack, getJet;
TextView bookInfo;
ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
//获取远程Service的代理对象
iBook = IBook.Stub.asInterface(service);
};
public void onServiceDisconnected(ComponentName name) {
iBook = null;
};
};
public void bind() {
Intent intent = new Intent();
intent.setAction("com.example.testaidlservice.BOOK_SERVICE");
bindService(intent, conn, BIND_AUTO_CREATE);
Toast.makeText(this, "绑定Service成功", Toast.LENGTH_LONG).show();
}
public void getData(Person owner) throws RemoteException {
//通过获得的代理对象访问Service中的数据
List books = iBook.getBooks(owner);
String name = owner.getName();
String result = "";
for(Book book:books) {
result = result + "书名:" + book.getBookName() + ",页数:" +book.getPageCount() + "\n";
}
bookInfo.setText(name + "的Book信息:\n" + result);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind = (Button)findViewById(R.id.bind);
getJack = (Button)findViewById(R.id.getJack);
getJet = (Button)findViewById(R.id.getJet);
bookInfo = (TextView)findViewById(R.id.bookInfo);
bind.setOnClickListener(this);
getJack.setOnClickListener(this);
getJet.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case R.id.bind:
bind();
break;
case R.id.getJack:
try {
getData(new Person(1, "Jack"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case R.id.getJet:
try {
getData(new Person(1, "Jet"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
客户端目录结构:
运行程序,效果如下图所示:
绑定到远程Service:
获取Person类的Book信息: