1、AIDL(Android interface definition language)
AIDL是Android进程间通信(IPC)的一种方式。每个进程都有自己独立的内存空间,一个进程不能访问另一个进程的内存空间,两个进程的数据交互需要通过进程间通信。AIDL通过绑定Service的方式,以实现两个App之间的交互。
2、AIDL支持的数据类型
Java的基本数据类型:byte,short,int,long,float,double,boolean,char
String和CharSequence
List和Map:元素必须是AIDL支持的数据类型
实现Parcelable的实体
AIDL生成的接口
3、定向Tag
in:数据只能由客户端流向服务端。使用in定向tag修饰参数,在服务端得到一个和实参值相同的对象(注意:不是同一个对象)。基本数据类型和String,CharSequence默认是in,而且只能是in。
out:数据只能由服务端流向客户端。
Inout:数据的流向是双向的。
4、AIDL文件
AIDL文件有两种类型
一种用于自定义数据类型,用于创建实现Parcelable的实体。
另一种用于定义接口。
5、oneway关键字
方法调用时,如果不想阻塞,而是直接返回,可以在定义方法时使用oneway关键字。例如:
oneway void sendSleep();
6、AIDL实现步骤
AIDL:
创建实现Parcelable的实体类
创建与实体类对应的.aidl文件
创建.aidl接口文件,并编译生成对应的.java文件(包含Binder)
服务端:
创建Service
在Service中创建Binder实例,并在onBind()中返回该实例
客户端:
实现ServiceConnection,获取BpBinder
调用bindService()
调用.aidl文件中已定义的接口,向服务端发送请求。
7、AIDL实例
为了了解AIDL的具体实现,实现了一个简单的例子。在本例当中,服务端进行书籍的管理,客户端可以向服务端查询或添加书籍。
开发环境为:Android Studio 3.2
7.1 创建Book.java
为了能够在客户端和服务端进行传递,需要实现Parcelable。
这里需要注意一下:多了一个readFromParcel方法,如果缺少这个方法,在定义AIDL接口是,Book类型的参数只能使用in定向Tag。
package com.zxd.demo.book;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public void readFromParcel(Parcel in) {
this.name = in.readString();
}
public Book() {
}
protected Book(Parcel in) {
this.name = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
7.2 创建Book.aidl文件
创建第一类AIDL文件。在工程中,选中app,点击右键,创建AIDL文件,会自动生成与java目录同一级别的aidl目录,并生成同名package,如下图所示。由于已经有同名的Book.java存在,创建Book.aidl时会报错,先起一个其他的文件名,然后再重命名。
Book.aidl文件内容很简单,如下:
// Book.aidl
package com.zxd.demo.book;
parcelable Book;
7.3 创建IBookService.aidl
创建第二类AIDL文件,也就是客户端与服务器端通信的接口。
可以看到,本例中使用了定向Tag和oneway 关键字。
在这里,我们import了Book和另一个文件IBookCallback,如果不进行import,编译会失败。编译代码之后,我们可以在generatedJava目录下看到自动生成的IBookService.java文件。
// IBookService.aidl
package com.zxd.demo.book;
import com.zxd.demo.book.Book;
import com.zxd.demo.book.IBookCallback;
interface IBookService {
List<Book> getBooks();
void addBookIn(in Book book);
void addBookOut(out Book book);
void addBookInOut(inout Book book);
void clearBookList();
void registerCallback(IBookCallback callback);
void unRegisterCallback(IBookCallback callback);
oneway void sendSleep();
}
7.4 服务端实现
在Service中创建IBookService.Stub实例,这就是服务端的BBinder。
package com.zxd.demo.book;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class BookService extends Service {
private final String TAG = this.getClass().getSimpleName();
private ArrayList<Book> mBookList = new ArrayList<>();
private RemoteCallbackList<IBookCallback> mRemoteCallbackList = new RemoteCallbackList<>();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "start id:" + startId);
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBookBinder;
}
//服务端的BBinder
private IBookService.Stub mBookBinder = new IBookService.Stub() {
@Override
public void registerCallback(IBookCallback callback) throws RemoteException {
mRemoteCallbackList.register(callback);
}
@Override
public void unRegisterCallback(IBookCallback callback) throws RemoteException {
mRemoteCallbackList.unregister(callback);
}
@Override
public List<Book> getBooks() throws RemoteException {
synchronized (this) {
return mBookList;
}
}
@Override
public void addBookIn(Book book) throws RemoteException {
synchronized (this) {
if (book != null) {
addBook(book);
}
}
}
@Override
public void addBookOut(Book book) throws RemoteException {
synchronized (this) {
if (book != null) {
book.setName("Little Prince");
} else {
book = new Book();
book.setName("Little Prince create");
}
addBook(book);
}
}
@Override
public void addBookInOut(Book book) throws RemoteException {
synchronized (this) {
if (book != null) {
book.setName("Little Prince");
} else {
book = new Book();
book.setName("Little Prince create");
}
addBook(book);
}
}
@Override
public void clearBookList() throws RemoteException {
synchronized (this) {
mBookList.clear();
broadcast();
}
}
@Override
public void sendSleep() throws RemoteException {
synchronized (this) {
try {
wait(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
broadcast();
}
}
private void addBook(Book book) {
mBookList.add(book);
broadcast();
}
private void broadcast() {
mRemoteCallbackList.beginBroadcast();
for (int i = 0; i < mRemoteCallbackList.getRegisteredCallbackCount(); i++) {
IBookCallback callback = mRemoteCallbackList.getBroadcastItem(i);
try {
callback.onBookChanged(mBookList);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mRemoteCallbackList.finishBroadcast();
}
};
}
7.5 客户端实现
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookService = IBookService.Stub.asInterface(service);
try {
mBookService.registerCallback(mBookCallbackBinder);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
try {
mBookService.unRegisterCallback(mBookCallbackBinder);
} catch (RemoteException e) {
e.printStackTrace();
}
mBookService = null;
}
};
//绑定BookService,flag设置为BIND_AUTO_CREATE,如果service尚未启动,会启动service。
Intent service = new Intent();
service.setAction("com.zxd.demo.book.SERVICE");
service.setPackage("com.zxd.demo.book");
bindService(service, mServiceConnection, BIND_AUTO_CREATE);
当客户端bindService并且连接成功后,在mServiceConnection中可以获得客户端的BpBinder。mBookService是IBookService.Stub.Proxy的实例,实现了接口IBookService,在IBookService.java中可以查看到这部分代码。
通过mBookService就可以调用AIDL接口定义的方法,实现与服务端的通信。
private void addBookIn() {
Book book = new Book();
book.setName("Black and Red " + mCount++);
try {
mBookService.addBookIn(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
7.6 Callback回调
试想这样一种情况:客户端调用接口,发起一个请求让服务端做一项耗时的任务,客户端不想阻塞等待服务端返回,但还想在此项任务完成时得到通知。这种异步情况在AIDL机制中怎么实现呢?这样就需要用到oneway关键字和RemoteCallbackList
7.6.1 定义CallBack接口
在图7-2中,我们看到有一个接口文件IBookCallback.aidl。客户端想得到通知就要实现这个接口并进行远程注册,当服务端返回时就能收到通知了。
// IBookCallback.aidl
package com.zxd.demo.book;
import com.zxd.demo.book.Book;
interface IBookCallback {
void onBookChanged(in List<Book> bookList);
void onWakeup();
}
7.6.2 定义注册Callback的方法和oneway方法
在本例中我们定义了如下方法:
void registerCallback(IBookCallback callback);
void unRegisterCallback(IBookCallback callback);
oneway void sendSleep();
这些方法定义在IBookService.aidl中,sendSleep方法因为使用了oneway关键字,当客户端调用时不会被阻塞。这就是服务端要做的耗时任务。前两个方法是用来注册和注销callback的。
7.6.3 服务端的实现
在服务端我们需要实现7.4.2定义的三个方法。
回顾BookService.java中的实现:RemoteCallbackList用于进行远程Callback的注册和注销。
在sendSleep()方法中,在执行结束后,进行回调处理。
private RemoteCallbackList<IBookCallback> mRemoteCallbackList = new RemoteCallbackList<>();
//服务端的BBinder
private IBookService.Stub mBookBinder = new IBookService.Stub() {
@Override
public void registerCallback(IBookCallback callback) throws RemoteException {
synchronized (this) {
mRemoteCallbackList.register(callback);
}
}
@Override
public void unRegisterCallback(IBookCallback callback) throws RemoteException {
synchronized (this) {
mRemoteCallbackList.unregister(callback);
}
}
@Override
public void sendSleep() throws RemoteException {
synchronized (this) {
try {
wait(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mRemoteCallbackList.beginBroadcast();
for (int i = 0; i < mRemoteCallbackList.getRegisteredCallbackCount(); i++) {
IBookCallback callback = mRemoteCallbackList.getBroadcastItem(i);
try {
//调用客户端的方法
callback.onWakeup();
} catch (RemoteException e) {
e.printStackTrace();
}
}
mRemoteCallbackList.finishBroadcast();
}
};
7.6.4 客户端的实现
在客户端实现Callback接口,并调用AIDL接口注册和注销CallBack。
当服务端发起回调时,客户端的mBookCallbackBinder的onWakeup()方法将被执行。
private IBookCallback.Stub mBookCallbackBinder = new IBookCallback.Stub() {
@Override
public void onWakeup() throws RemoteException {
Log.d(TAG, "server wake up");
}
};
当与服务端建立连接时,注册Callback。连接断开后,注销Callback。
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookService = IBookService.Stub.asInterface(service);
try {
mBookService.registerCallback(mBookCallbackBinder);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
try {
mBookService.unRegisterCallback(mBookCallbackBinder);
} catch (RemoteException e) {
e.printStackTrace();
}
mBookService = null;
}
};
8 小结
AIDL是进程间通信的一种方法,它是Android Binder机制的运用,客户端和服务端每一次的通信都要通过底层的Binder驱动,在同一个App内部不建议使用。