项目地址:项目地址包含之前的内容
上个文章介绍了IBinder和Messenger的使用。而且也说了,Messenger底层也是使用了AIDL。下面笔记一下AIDL。
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。相信这个解释,可以在很多文章中看到。所以也就知道为啥不在家.java文件,而是.aidl格式的文件。其中语法呢,大家可以去了解一下,不过和java大同小异。
下面有一个场景,咱们现在有很多的书,Book类,书有名字。我现在需要一个BookManager类来管理这些书,如:获取所有的书籍、添加一本书、根据一个名字找到一本书。
正常情况下,会分为两步
- 建一个Book类
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "book name:" + name;
}
}
- 建一个BookManager类
public class BookManager{
{
private List<Book> bookList=new ArrayList();
public List<Book> getBookList() throws RemoteException {
return bookList;
}
public void addBook(Book book) throws RemoteException {
bookList.add(book);
}
public Book getBookByName(String name) throws RemoteException {
if (bookList.size()==0){
return null;
}
for (Book b : bookList) {
if (b.getName().equals(name)){
return b;
}
}
return null;
}
};
}
通过这两步咱们就可以进行控制,Book的一些内容了。
但是,这种传统的方法并不能提供夸进程的数据共享。所以呢就需要借助AIDL了。好,这个时候我们认为,AIDL能够实现夸进程。那我们把上面两个文件变成对应的AIDL文件不就好了。
- 新建一个要处理的数据类,如:Book.java。
- 新建一个AIDL,接口名字和要处理的数据类保持一致。如:Book.aidl。
- 新建一个操作的AIDL,会定义一些具体的操作方法。如:BookManager.aidl。
通过上面三步,我们建好了,对应的文件,但是真正运行的时候应用只能加载java文件,怎么办呢,所以我们会有第一步,先创建好要处理的数据模型类。Book.aidl会和Book.java对应。
现在有了,程序可以加载的Book.java,但是没有BookManager.java,接下来就要看BookManager.aidl的了,AS会自动帮我们生成对应的java文件。
看代码:
Book类
package com.example.study.entity;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "book name:" + name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
}
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];
}
};
}
进程间传递数据需要序列化,所以这里实现了Parcelable 接口。
Book.aidl
// com.example.com.example.study.entity.Book.aidl
package com.example.study.entity;
parcelable Book;
// Declare any non-default types here with import statements
指定了序列化的类。切记,这里面就这么多内容,其它自动生成部分删除掉。
BookManager.aidl
// BookManager.aidl
package com.example.study;
// Declare any non-default types here with import statements
import com.example.study.entity.Book;
interface BookManager {
List<com.example.study.entity.Book> getBookList();
void addBook(in com.example.study.entity.Book book);
com.example.study.entity.Book getBookByName(in String name);
}
aidl默认的目录和java同级,包名就是项目的包名。需要注意,Book.java和Book.aidl的他们的包名必须保持一致
当build一下,就会出现一个BookManager.java文件,目录在
…\app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\com\example\study\BookManager.java
package com.example.study;
public interface BookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
....
}
public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.study.entity.Book book) throws android.os.RemoteException;
public com.example.study.entity.Book getBookByName(String name) throws android.os.RemoteException;
}
可以看到生成了一个对应的BookManager接口,定义的方法都在,多了一个Stub类,这个类继承了Binder,实现了BookManager接口。至于Stub类,先不管它,根据方法再去看。
public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
private static final String DESCRIPTOR = "com.example.study.BookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.study.BookManager interface,
* generating a proxy if needed.
*/
public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
return ((com.example.study.BookManager) iin);
}
return new com.example.study.BookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
....
private static class Proxy implements com.example.study.BookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
......
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBookByName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
建一个AIDLIService
public class AIDLService extends BaseService {
private List<Book> bookList;
@Override
public void onCreate() {
super.onCreate();
bookList = new ArrayList<>();
}
@Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
return stub.asBinder();
}
final BookManager.Stub stub = new BookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return bookList;
}
@Override
public void addBook(Book book) throws RemoteException {
bookList.add(book);
}
@Override
public Book getBookByName(String name) throws RemoteException {
if (bookList.size()==0){
return null;
}
for (Book b : bookList) {
if (b.getName().equals(name)){
return b;
}
}
return null;
}
};
}
Service中我们实例化了一个stub对象。在onBind的时候返回了stub.asBinder(),这个方法有没有很熟悉的感觉,Messenger.getBinder(),最终调用的是IMessenger的asBinder方法。所以说Messenger是AIDL的一种封装,这里也可以返回stub因为他继承了Binder。
Activity中:
/**
* 书本管理对象
*/
private BookManager manager;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//建立连接
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBind = true;
Log.i("life", "ServiceConnection-----onServiceConnected");
//得到接口对象
manager= BookManager.Stub.asInterface(service);
}
...
};
}
...
//点击事件
case R.id.bt_1:
try {
List<Book> bookList = manager.getBookList();
tv.setText(bookList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.bt_2:
try {
manager.addBook(new Book("归隐"));
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.bt_3:
try {
Book book = manager.getBookByName("归隐");
tv.setText(book.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
break;
....
ok,到这里就完成了夸进程通信了,当然不夸进程这种方式也能进行通信。
这里是在本应用内直接使用的,所以没有进行拷贝之类的操作。
如果新建一个应用的话,只需要将Book.java,和生成后的BookManager.java拷贝过去就行了。切记包名和原来保持一致。
Stub浅析
首先我们看一下,调用了Stub的那些方法:
- stub.asBinder(),(其实就是stub自己)
- BookManager.Stub.asInterface(service)
我们只用了上面两个方法,先看一下第一个
public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
...
@Override
public android.os.IBinder asBinder() {
return this;
}
...
可以看出,返回的就是它本身。而且Stub是一个抽象类,并没有实现BookManager的方法。
第二个方法
public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
....
/**
* Cast an IBinder object into an com.example.study.BookManager interface,
* generating a proxy if needed.
*/
public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//得到IBinder上的接口对象,这里是BookManager
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
return ((com.example.study.BookManager) iin);
}
//重新实例化一个
return new com.example.study.BookManager.Stub.Proxy(obj);
}
先通过IBinder去得到BookManager接口对象,如果没有实例化一个Proxy对象,看下Proxy
private static class Proxy implements com.example.study.BookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
....
代理类Proxy实现了BookManager接口,里面有个成员变量IBinder。并且实现了BookManager的所有方法。也就是说真正的方法实现还是Proxy中,那么Proxy的方法是怎么被回调的呢?
getBookList方法为例
@Override
public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException {
//客户端请求参数
android.os.Parcel _data = android.os.Parcel.obtain();
//服务端返回的数据
android.os.Parcel _reply = android.os.Parcel.obtain();
//返回结果
java.util.List<com.example.study.entity.Book> _result;
try {
//设置Token确实是哪个接口
_data.writeInterfaceToken(DESCRIPTOR);
//发送RPC请求
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
//获取返回结果
_result = _reply.createTypedArrayList(com.example.study.entity.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
IBinder机制发送RPC请求会调用到服务端的Stub 的onTransact
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
...
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
//调用service的方法,并返回结果
java.util.List<com.example.study.entity.Book> _result = this.getBookList();
reply.writeNoException();
//序列化结果
reply.writeTypedList(_result);
return true;
}
....
Stub的this.getBookList()方法,我们在AIDLService中已经实现了。返回结果后交给reply进行序列化。
其中code是用来确定调用哪个方法的,毕竟方法有很多。
总结 :
AIDL可以实现夸进程通信。
语法参数有in 、out、inout,三种。
多任务并发执行,需要考虑线程安全问题。
避免耗时操作,阻塞UI线程。