文章首发于我的博客java
参考:android
对于AIDL, 我不是很熟悉, 由于在工做中没有用到过.可是AIDL确实是Android跨进程通讯中最多见的方式, 因此学习一下是十分有必要的.git
AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成能够在 Android 设备上两个进程之间进行进程间通讯 (interprocess communication, IPC) 的代码。若是在一个进程中(例如 Activity)要调用另外一个进程中(例如 Service)对象的操做,就可使用 AIDL 生成可序列化的参数,来完成进程间通讯。 简言之,AIDL 可以实现进程间通讯,其内部是经过 Binder 机制来实现的。github
首先创建一个AIDL文件IBookManager.aidl 这个文件咱们先不去管, 待会儿再来修改. 定义一个Book类, 实现Parcelable接口.网络
public class Book implements android.os.Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public Book() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}
protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = 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];
}
};
}
复制代码
AIDL支持的数据类型:并发
虽然可使用 ArrayList 与 HashMap, 可是其类型也只能用被AIDL支持的数据类型. 自定义的Parcelable对象和AIDL对象必需要显式的import进来
因此最终IBookManager的实现:ide
// IBookManager.aidl
package me.mundane.testaidl.aidl;
// Declare any non-default types here with import statements
// 显式的导入
import me.mundane.testaidl.aidl.Book;
interface IBookManager {
/** * 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);
void addBook(in Book book);
List<Book> getBookList();
}
复制代码
另外, 若是AIDL文件中用到了自定义的Parcelable对象, 必须新建一个和它同名的AIDL文件, 并在其中声明它为Parcelable类型高并发
// IBookManager.aidl
package me.mundane.testaidl.aidl;
// Declare any non-default types here with import statements
parcelable Book;
复制代码
除此以外, AIDL中除了基本数据类型, 其余类型的参数必须标上方向: in表示输入型参数, out表示输出型参数, inout表示输入输出型参数 为了方便AIDL的开发, 建议把全部和AIDL相关的类和文件所有放入一个包中, 方便整个复制
看一下个人分包结构 如今咱们Make Project, Android Studdio自动为咱们生成Binder类
该类中有个重要的内部类Stub, 继承了Binder类, 并实现了IBookManager接口
post
为了实现进程间的通讯, 咱们须要建立一个独立进程的service, 来模拟进程间的通讯, service充当服务端 ":remote"表示在当前的进程名前面附加上当前的包名, 而且是当前应用的私有进程学习
<service android:process=":remote" android:name=".aidl.BookManagerService" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="me.mundane.testaidl.aidl.BookManagerService"/>
</intent-filter>
</service>
复制代码
在service中定义一个我上面说的那个重要的内部类IBookManager.Stub的对象, 并定义具体实现
private IBookManager.Stub mBinder = new Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
final int bookCount = mBookList.size();
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(BookManagerService.this,
String.format("添加了一本新书, 如今有%d本", bookCount), Toast.LENGTH_SHORT).show();
}
});
Log.d(TAG, String.format("添加了一本新书, 如今有%d本", bookCount));
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
};
复制代码
当Activity调用bindService()方法时, 会回调Service中的onBind()方法, 在onBind()方法中将上面定义的mBinder对象返回
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
return mBinder;
}
复制代码
这样Activity就能获得Stub实现对象, 这个对象是个Binder对象, 能够实现进程间通讯.
Activity充当服务端, 首先须要绑定service
public void bindService(View view) {
Intent intent = new Intent();
intent.setAction("me.mundane.testaidl.aidl.BookManagerService");
intent.setPackage(getPackageName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
复制代码
注意这个mServiceConnection是咱们自定义的
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBookManager = IBookManager.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
复制代码
能够看到在onServiceConnected()方法里返回了咱们获得了一个IBookManager对象, 这其实就是service.onBind()方法中返回的Binder对象, 利用这个Binder对象咱们既能够随心所欲了, 好比addBook()
public void addBook(View view) {
if (mIBookManager != null) {
try {
mIBookManager.addBook(new Book(18, "漫画书"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
复制代码
效果图
并且能够看到, 确实起了两个进程, 实现的是进程间通讯
下面来演示一下真正的两个应用之间的通讯
首先新建一个aidlserver的module 建立AIDL文件, 定义一个login()方法
// IMyAidlInterface.aidl
package me.mundane.aidlserver;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/** * 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);
void login(String userName, String password);
}
复制代码
Make Module, 和上面同样, Android Studio会自动生成一个IMyAidlInterface的.java文件 新建一个Service, 在onBind()的时候返回一个继承了IMyAidlInterface.Stub的binder对象
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
public AIDLService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public interface OnLoginListener {
void login(String username, String password);
}
private OnLoginListener mOnLoginListener;
public void setOnLoginListener(OnLoginListener listener) {
mOnLoginListener = listener;
Log.d(TAG, "mOnLoginListener = " + mOnLoginListener);
}
class MyBinder extends IMyAidlInterface.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void login(String userName, String password) throws RemoteException {
Log.d(TAG, "mOnLoginListener = " + mOnLoginListener);
if (mOnLoginListener != null) {
mOnLoginListener.login(userName, password);
}
}
public AIDLService getService() {
return AIDLService.this;
}
}
}
复制代码
在AndroidManifest为Service设置默认的category和action, 使得可以经过隐式启动来启动这个Service
<service android:name=".AIDLService" >
<intent-filter>
<action android:name="me.mundane.aidlserver"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
复制代码
再看一下咱们在MainActivity中的逻辑
public class MainActivity extends AppCompatActivity implements OnLoginListener {
private TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = findViewById(R.id.tv);
Intent intent = new Intent(this, AIDLService.class);
bindService(intent, mAIDLConnection, Service.BIND_AUTO_CREATE);
}
private ServiceConnection mAIDLConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBinder binder = (MyBinder) service;
AIDLService aidlService = binder.getService();
aidlService.setOnLoginListener(MainActivity.this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Handler mHandler = new Handler();
@Override
public void login(final String username, final String password) {
mHandler.post(new Runnable() {
@Override
public void run() {
mTv.setText(username + ", " + password);
Toast.makeText(MainActivity.this, "登陆成功", Toast.LENGTH_SHORT).show();
}
});
}
}
复制代码
接在再建一个Client的module 把服务端 aidl 整个文件夹拷贝到客户端 src/main 目录下, 一样的, Make Project一下. 接着再完善一下MainActivity中逻辑, 首先经过bindService(), 在onServiceConnected()的时候获取一个binder对象, 在点击登陆按钮的时候将用户名和密码传递过去.
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface mIMyAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
// 服务端AndroidManifest.xml文件该Service所配置的action
intent.setAction("me.mundane.aidlserver");
// Service所在的包名
intent.setPackage("me.mundane.aidlserver");
bindService(intent, new ConnectCallBack(), Context.BIND_AUTO_CREATE);
}
public void login(View view) {
if (mIMyAidlInterface != null) {
try {
mIMyAidlInterface.login("mundane", "123456");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
class ConnectCallBack implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyAidlInterface = null;
}
}
}
复制代码
看一下效果, 能够看到在Client端传递了用户名和密码, 在Server端获取了用户名和密码, 实现了两个应用之间的通讯, 很显然是真正的跨进程通讯
下面以第一个Book的例子浅析一下AIDL的原理 首先看服务端的这段代码
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
return mBinder;
}
private IBookManager.Stub mBinder = new Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
final int bookCount = mBookList.size();
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(BookManagerService.this,
String.format("添加了一本新书, 如今有%d本", bookCount), Toast.LENGTH_SHORT).show();
}
});
Log.d(TAG, String.format("添加了一本新书, 如今有%d本", bookCount));
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
};
复制代码
咱们从源码解析的角度, 看一下这个Stub的构造方法
public static abstract class Stub extends android.os.Binder implements IBookManager {
private static final String DESCRIPTOR = "me.mundane.testaidl.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
...
复制代码
能够看到这个内部类Stub是一个抽象类, 跟踪this.attachInterface(this, DESCRIPTOR);
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
复制代码
能够看到只是将它本身自己和DESCRIPTOR这个字符串标识做为Binder中的全局变量保存了起来.DESRIPTOR做为Binder 的惟一标识,通常用当前 Binder 的全类名表示。
而后咱们看客户端的这段代码
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBookManager = IBookManager.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
复制代码
跟踪IBookManager.Stub.asInterface(service);
这个方法
/** * Cast an IBinder object into an me.mundane.testaidl.aidl.IBookManager interface, * generating a proxy if needed. */
public static IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new Proxy(obj);
}
复制代码
注释是说将IBinder接口对象强转为IBookManager对象, 若是须要的话, 生成一个代理. 首先会根据标识符去 IBinder 的本地去查找是否有该对象,也就是调用 obj.queryLocalInterface(DESCRIPTOR) 方法,继续源码中 Binder.java
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
复制代码
意思就是若是本地存在这个标识符的 IInterface 对象,那就直接返回以前构造方法中初始化的 mOwner 对象,不然返回 null.由于咱们这里涉及到了跨进程通讯,虽然服务端在初始化mBinder进行了attachInterface(this, DESCRIPTOR)
(能够再看看上面的源码), 但那时另外一个进程的, 因此这里会直接返回 null。接着就return一个 new Proxy(obj);
, 咱们继续跟踪这个Proxy类
private static class Proxy implements IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void addBook(Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<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<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
复制代码
在客户端中, 咱们调用的是IBookManager的addBook()方法, 也就是这个Proxy对象的addBook()方法, 观察这个addBook()方法. 这里就涉及到了一个重要的类 Parcel,Parcel 天生具有跨进程传输数据能力. 把须要传递的数据写入 Parcel 中,而后到达目标进程后,将 Parcel 中的数据读出便可,因此能够将 Parcel 称为数据传输载体。 这里的_data就是一个Parcel对象, 咱们将Book写入Parcel, 而后调用mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
这个mRemote其实就是以前传进来的也就是Service返回的一个IBookManager.Stub对象, 咱们仍是再跟踪一下这个transact()方法吧, 在Binder类中
/** * Default implementation rewinds the parcels and calls onTransact. On * the remote side, transact calls into the binder to do the IPC. */
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
复制代码
能够看到onTransact()方法会被调用, 这里实际上是这样 client 端:BpBinder.transact() 来发送事务请求; server 端:BBinder.onTransact() 会接收到相应事务。 因此服务端的onTransact()方法会被调用, 其实就是IBookManager.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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
复制代码
case TRANSACTION_addBook的时候, 调用addBook()方法, 这样天然就完成了服务端中收到响应以后的操做.返回true表示这个事务是成功的. 当 onTransact 返回 true,调用成功,从 reply 对象中取出返回值,返回给客户端调用方。
高度归纳 AIDL 的用法,就是服务端里有一个 Service,给与之绑定 (bindService) 的特定客户端进程提供 Binder 对象。客户端经过 AIDL 接口的静态方法asInterface 将 Binder 对象转化成 AIDL 接口的代理对象,经过这个代理对象就能够发起远程调用请求了。 用这张图来表述再清楚不过了. 值得一提的是, 在这里AIDL是系统自动生成的, 实际上咱们手写AIDL也是彻底能够的, 只要彻底理解Binder的原理, 只不过使用系统生成更加方便快速而已. 下面总结一下几种常见的IPC方式
名称 | 优势 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据类型 | 四大组件的进程间通讯 |
文件共享 | 简单易用 | 不适合高并发场景, 而且没法作到进程间的即时通讯 | 无并发访问情形, 交换简单的数据, 实时性不高的场景 |
AIDL | 功能强大, 支持一对多并发通讯, 支持实时通讯 | 使用稍复杂, 须要处理好线程同步 | 一对多通讯且有RPC需求 |
Messenger | 功能通常, 支持一对多串行通讯, 支持实时通讯 | 不能很好处理高并发情形, 不支持RPC, 数据经过Message进行传输, 所以只能传输Bundle支持的数据类型 | 低并发的一对多即时通讯, 无RPC需求, 或者无须要返回结果的RPC需求 |
ContentProvider | 在数据源访问方便功能强大, 支持一对多并发数据共享, 可经过Call方法扩展其余操做 | 能够理解为受约束的AIDL, 主要提供数据源的CURD操做 | 一对多的进程间的数据共享 |
Socket | 功能强大, 能够经过网络传输字节流, 支持一对多并发实时通讯 | 实现细节有点繁琐, 不支持直接的RPC | 网络数据交换 |
关于Binder, 若是想了解更多一些, 我推荐weishu的这一篇Binder 学习指南