转载请标明出处:
http://blog.csdn.net/qq_27495349/article/details/53338273
本文出自:【CF凌晨的博客】
引言
hello,大家好,一周马上又要过去,不知道大家有没有想我哈!话说南京下雪了,不知道大家有没有看到雪哈,今年的雪来的有点早哈!!!
AIDL介绍
AIDL是Android Interface definition language缩小,意思是android接口定义语言,用于进程间通讯。
AIDL创建
那么我们怎么编写AIDL呢?首先我们先打开AS,然后创建一个项目,创建好之后,我们直接创建AIDL文件,如图
直接finish就好了。创建之后,我们来看看生成的文件。
可以看到一个aidl的文件我们就创建好了。
AIDL数据传递类型
从官方的介绍来看,大体意思如下:
1.所有java的基本数据类型(short不支持)
2.String
3.CharSequence
4.List(list里面存放的必须是允许的数据类型)
5.map(同上)
6.Parcelable
那我们可以传自定义的数据么?答案是可以的。
首先我们在aidl包下面创建一个Book类,如下
package com.lingchen.testaidl;
/**
* Author lingchen
* Email 838878458@qq.com
* Time 2016/11/25
* Function 书
*/
public class Book {
//名字
private String name;
//价格
private float price;
}
我们实现Parcelable这个接口。这里有个插件,可以快速生成 Parcelable,名字是(Android Parcelable Code Generator)。
package com.lingchen.testaidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Author lingchen
* Email 838878458@qq.com
* Time 2016/11/25
* Function 书
*/
public class Book implements Parcelable {
//名字
private String name;
//价格
private float price;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeFloat(this.price);
}
public Book() {
}
protected Book(Parcel in) {
this.name = in.readString();
this.price = in.readFloat();
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
这就是我们最终的代码。那我们把IMyAidlInterface删掉,重新创建一个IBookAidlInterface.aidl文件,内容如下:
// IBookAidlInterface.aidl
package com.lingchen.testaidl;
// Declare any non-default types here with import statements
interface IBookAidlInterface {
List<Book> getBook(Book book);
}
到这里我们编译下,结果如下
竟然没有找到,是不是我们没有写import?我们加上
import com.lingchen.testaidl.Book;
运行还是错误。其实aidl是根本不认识的,那怎么让他认识呢?我们创建一个Book.aidl文件,如下:
// Book.aidl
package com.lingchen.testaidl;
//申明Book
parcelable Book ;
很简单的几句话,我们再来运行:
这里我们要知道一个知识点,因为aidl底层传递数据,要分 打包与解包,这样是很消耗资源的,那如果我们指定他是输入,那么就会让效率提高,所以我们加上in:
List<Book> getBook(in Book book);
再次编译,好了,终于编译通过了。
实现AIDL,并且调用
我们的aidl文件已经写好了,那我们现在写个service类来实现我们的aidl。接代码
package com.lingchen.testaidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Author lingchen
* Email 838878458@qq.com
* Time 2016/11/25
* Function 远程服务
*/
public class MyRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return bookBinder;
}
private List<Book> bookList = new ArrayList<>();
private IBinder bookBinder = new IBookAidlInterface.Stub() {
@Override
public List<Book> getBook(Book book) throws RemoteException {
bookList.add(book);
return bookList;
}
};
}
别忘了在manifest里面添加
<!--添加服务 指定在remote进程-->
<service android:name=".MyRemoteService"
android:process=":remote"
/>
好了,我们的远程服务已经搞定了,那我们怎么开启呢?看代码
package com.lingchen.testaidl;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends Activity {
private TextView bookSizeTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bookSizeTv = (TextView) findViewById(R.id.book_size);
bindRemoteService();
}
private void bindRemoteService() {
Intent intent = new Intent(this, MyRemoteService.class);
//如果只知道他的包跟类名
//intent.setComponent(new ComponentName("com.lingchen.testaidl", "com.lingchen.testaidl.MyRemoteService"));
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
//远程服务
private IBookAidlInterface iBookAidl;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到远程服务
iBookAidl = IBookAidlInterface.Stub.asInterface(service);
bookSizeTv.setText("连接成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookSizeTv.setText("连接异常");
}
};
/**
* 添加书
*/
public void addBook(View view) {
if (iBookAidl != null) {
try {
List<Book> books = iBookAidl.getBook(new Book("凌晨", 998));
bookSizeTv.setText(String.valueOf(books.size()));
} catch (RemoteException e) {
bookSizeTv.setText("连接异常");
e.printStackTrace();
}
}
}
}
好了xml我就不发了 很简单,那么。到现在为止,我们已经完成了跨进程通信。
AIDL原理解析
我们前面也说过,通过aidl自动生成的java文件是我们的重点,我们分析的原理也要通过他来讲解,首先我们来看看它:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\AndroidStudioProjects\\TestAidl\\app\\src\\main\\aidl\\com\\lingchen\\testaidl\\IBookAidlInterface.aidl
*/
package com.lingchen.testaidl;
public interface IBookAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.lingchen.testaidl.IBookAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.lingchen.testaidl.IBookAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lingchen.testaidl.IBookAidlInterface interface,
* generating a proxy if needed.
*/
public static com.lingchen.testaidl.IBookAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lingchen.testaidl.IBookAidlInterface))) {
return ((com.lingchen.testaidl.IBookAidlInterface) iin);
}
return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBook: {
data.enforceInterface(DESCRIPTOR);
com.lingchen.testaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lingchen.testaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.util.List<com.lingchen.testaidl.Book> _result = this.getBook(_arg0);
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lingchen.testaidl.IBookAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lingchen.testaidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException;
}
很多人看到这个都会产生恐惧,既然我们要想变强,那就要去看。好了,来看看下面的代码
这里我们看到了,其实大体的就是一个接口,一个内部类,一个方法,这个方法就是我们代码中定义的方法,这下我们看得清晰了吧。
这里有一个Stub静态内部类,这个名字大家一看到,就感觉很熟悉,没错
//拿到远程服务
iBookAidl = IBookAidlInterface.Stub.asInterface(service);
那我们可以肯定,这个内部类里面一定有一个方法是asInterface();我们先不着急看,先来看看我通过注释来翻译之后的代码
好了,我们发现,里面包含了一个描述符(我们可以猜想,这个可能就是标识这个服务的)果不其然,下面的构造方法就看到了一个方法attachInterface方法,那么我们点进去看看是干什么的
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
* to return the given owner IInterface when the corresponding
* descriptor is requested.
*/
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
意思大体就是讲一个接口跟descriptor粘合,可以通过方法queryLocalInterface来获取到。
我们再往下面看
/**
* 铸造一个内部对象com.lingchen.testaidl。如果需要IBookAidlInterface接口,生成一个代理.
*/
public static com.lingchen.testaidl.IBookAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//通过queryLocalInterface查找IBookAidlInterface
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lingchen.testaidl.IBookAidlInterface))) {
return ((com.lingchen.testaidl.IBookAidlInterface) iin);
}
//如果没找到 直接返回Proxy
return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
}
这个方法大家肯定晓得,上面已经提到了,里面也提到了我们所说的queryLocalInterface方法。
这里我们看到了如果没有找到,那么将直接获取Proxy(代理)。
这个东西是什么呢?,我们先不管,我们继续往下面看asBinder这个方法我们就不说了,就是返回stub。
下面是onTransact方法,可以从字面上看到 是交易的意思,我想这可能就是最主要的方法,毕竟我们跨进程交互本来就是一场交易。
下面是Proxy类,也是一个静态内部类,这里我们可以看到一个getBook方法,直接上代码
@Override
public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException {
//从池中检索一个新的包裹对象
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lingchen.testaidl.Book> _result;
try {
//写入token
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
//把数据写入包裹里
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//底层开始发送,stub就会接收到
mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
_reply.readException();
//获取数据
_result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这里有些同学都应该知道了,我们最终调用iBookAidl.getBook(new Book(“凌晨”, 998))的时候,其实我们就是在调用这个方法,原理看下面图
//拿到远程服务的代理 return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
iBookAidl = IBookAidlInterface.Stub.asInterface(service);
//通过代理调用getBook方法
List<Book> books = iBookAidl.getBook(new Book("凌晨", 998));
是不是一下子清晰了?那么我们只要研究这个Proxy中getBook方法我们就知道了。
上面我们已经放出了Proxy中的getBook方法,我这里在放一次
//从池中检索一个新的包裹对象用来存放book
android.os.Parcel _data = android.os.Parcel.obtain();
//从池中检索一个新的包裹对象用来接受交易完成之后的数据
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lingchen.testaidl.Book> _result;
try {
//写入token
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
//把数据写入包裹里
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//底层开始发送,stub就会接收到 0-》单向的RPC( Remote Procedure Call Protocol)——远程过程调用协议
mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
_reply.readException();
//获取数据
_result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
我们先来看这句代码
//把数据写入包裹里
book.writeToParcel(_data, 0);
这里的代码,大家应该可能会想到了,我们当初实现了Parcelable不是让实现了这个方法么?
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeFloat(this.price);
}
可以看到,就是往包裹中写入数据,也就是_data包裹。
继续看下面的方法mRemote.transact这个方法其实就通知交易可以开始了,就是回调我们到我们stub里面的transact方法,可以看到,方法里面传进了
Stub.TRANSACTION_getBook(交易代号)
android.os.Parcel _data 数据包裹
android.os.Parcel _reply 接收数据的包裹
0 单向的RPC( Remote Procedure Call Protocol)——远程过程调用协议
这里我们就来看一下Stub中的onTransact方法
//交易
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBook: {
data.enforceInterface(DESCRIPTOR);
com.lingchen.testaidl.Book _arg0;
if ((0 != data.readInt())) {
// book中 return new Book(source);
_arg0 = com.lingchen.testaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.util.List<com.lingchen.testaidl.Book> _result = this.getBook(_arg0);
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
看到这个方法,大家看代码其实已经晓得了,其实就是回调了我们book中CREATOR
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
好了,aidl的源码分析到这里结束了。
总结
aidl是我们android 进程间通讯的语言。我们通过IBookAidlInterface.Stub.asInterface(service)拿到的是他的代理类。
我们也知道了aidl的传输的数据类型以及自定义的数据类型,并且我们也知道了为啥要实现Parcelable方法。
好了,这周即将结束,也希望大家能周末愉快,谢谢大家!我们下次再见!!!