今天来详讲一种Android进程通信的方式:AIDL
AIDL:android接口定义语言
AIDL的作用:首先我们知道,每个进程都运行在自己的虚拟机中,拥有自己独立的内存地址空间,在自己的内存空间操作自己的数据,但如果想跟其它内存空间通信时。这时就涉及到进程之间的通信,aidl就是它们通信的桥梁之一,它制定自己的规则,只要按照它的规则来就可以实现进程之间的通信。
为什么选用AIDL来进行进程之间的通信:
(1)Messenger时串行方式处理客户端发来的消息,如果有大量消息同时发送到服务端,Messenger只能一条一条道处理。但是如果有大量并发请求,那么Messenger就不行了。另外Messenger主要是传送消息用的,如果我们想调用服务端的方法时,这个时候就要AIDL 了。
(2)BroadcastReceive也可以进行进程之间的通信,但是BroadcastReceive占用的系统资源太多,显然如果频繁的调用的话是不合适的。
AIDL所能使用的数据类型:
(1) java八种基本类型:int ,short , long , byte , char , Boolean , float ,double .
(2) List的ArrayList.
(3) Map的HashMap
(4) 实现了parcelable的对象
(5) CharSequence、string类型。
定向tag:分别有in、out、inout。这里选择的参考对象是服务端。In表示数据只能从客户端流向服务端。Out表示数据只能从服务端流向客户端。Inout可以双向流动,inout使用比较消耗系统资源。
下面跟着例子讲一下AIDL通信原理:
项目结构:
1、新建一个aidl文件:IBookManager.aidl
// IBookManager.aidl
package com.ryg.ipcaidl;
import com.ryg.ipcaidl.Book;//因为下面用到Book这个类,所以这里一定要import Book。
interface IBookManager {
List<Book> getBookList();//因为这里用到Book这个类,所以一定要新建一个Book.aidl文件
void addBook(in Book book);
}
2、Book.aidl
package com.ryg.ipcaidl;
parcelable Book;
3、此时ReBuild一下,这时那会发现工程下多了一个IBookManager.java的接口文件
在这里来个一个小插曲:
有可能那在rebuild的时候,会报以下错误:
解决办法有两个:
(1)修改 build.gradle 文件:在 android{} 中间加上下面的内容:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
(2)或者将你写的Book.java文件放在项目的包路径下。
4、这里来详细解说一下IBookManager.java这个文件:
其实这个时候可以将之前的aidl文件删除,它的作用已经用完,哈哈。
默认生成的BookManagerImpl.java文件是这样的:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/apple/workSpace/IPCAidl/app/src/main/aidl/com/ryg/ipcaidl/IBookManager.aidl
*/
package com.ryg.ipcaidl;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ryg.ipcaidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.ryg.ipcaidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ryg.ipcaidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.ryg.ipcaidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ryg.ipcaidl.IBookManager))) {
return ((com.ryg.ipcaidl.IBookManager)iin);
}
return new com.ryg.ipcaidl.IBookManager.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.ryg.ipcaidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.ipcaidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.ryg.ipcaidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.ryg.ipcaidl.IBookManager
{
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.ryg.ipcaidl.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.ryg.ipcaidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.ryg.ipcaidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.ryg.ipcaidl.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();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.ryg.ipcaidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.ryg.ipcaidl.Book book) throws android.os.RemoteException;
}
我们可以把这里面的代码整理一下,便于分析(重点:分析过程已经在代码的注释里面说明,请大家仔细阅读):
package com.ryg.ipcaidl;
import android.media.audiofx.AudioEffect;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import java.util.List;
import static com.ryg.ipcaidl.IBookManager.Stub.DESCRIPTOR;
import static com.ryg.ipcaidl.IBookManager.Stub.TRANSACTION_getBookList;
/**
* Created by apple on 17/5/7.
*/
public interface IBookManager extends IInterface{
//创建一个接口对象Stub
public static abstract class Stub extends Binder implements IBookManager{
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
//这个方法运行在服务端的binder线程池中,将服务端的binder方法转化为客户端所能识别的aidl接口
public static IBookManager asInterface(IBinder obj){
if (obj == null){
return null;
}
return new IBookManager.Stub().Proxy(obj);
}
//用于返回当前的binder对象
@Override
public IBinder asBinder() {
return this;
}
//该方法运行在 客户端
private static class Proxy implements IBookManager{
private Binder mRemote;
Proxy(IBinder remote){
}
@Override
public List<Book> getBookList() throws RemoteException {
//创建输入型Parcel对象
Parcel data = Parcel.obtain();
//创建输出型Parcel对象
Parcel reply = Parcel.obtain();
List<Book> result;
//把参数信息写入data中
data.writeInterfaceToken(DESCRIPTOR);
//调用transact方法发起远程请求,客户端当前线程挂起,服务端此时调用onTransact方法
mRemote.transact(TRANSACTION_getBookList,data,reply,0);
//获取rpc过程中的返回值
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
//并将该返回值返回,当前线程继续执行
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
}
@Override
public IBinder asBinder() {
return null;
}
}
//这个方法运行在 服务端 的binder线程池中,当客户端发起远程请求时,会将远程请求通过系统底层封装后,交由此方法处理
//这个方法有四个参数。code:客户端请求的目标方法是什么。data:取出目标方法所需要的参数。repl:执行完毕以后向reply中写数据
//注意这个方法返回的是boolean类型,如果这个方法返回的是fals,说明此客户端没有权限访问这个服务端
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
//具体方法
@Override
public List<Book> getBookList() throws RemoteException {
return null;
}
//具体方法
@Override
public void addBook(Book book) throws RemoteException {
}
}
}
5、原理看完以后继续,看我们实际当中怎么使用它,在本实例中客户端代码如下:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.List;
public class BookClientActivity extends AppCompatActivity {
private static final String TAG = "BookClientActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动服务端
Intent intent = new Intent();
intent.setAction("com.ryg.ipcaidl.launch");
intent.setPackage(getPackageName());
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
//获得与服务端链接
private ServiceConnection mConnection =
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//调用运行在服务端的asinterface()方法
//将服务端的binder对象转化为客户端所需要的aidl接口对象
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
//直接调用服务端方法
List<Book> list = bookManager.getBookList();
Log.e(TAG,"获取所有书的list :"+list.toString());
Book newBook = new Book(3,"客户端添加的书3");
bookManager.addBook(newBook);
list = bookManager.getBookList();
Log.e(TAG,"客户端添加以后 获取所有书的list :"+list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
客户端使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用
6、服务端代码如下:
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by apple on 17/5/7.
*/
public class BookManagerService extends Service{
private static final String TAG = "BookManagerService";
//用CopyOnWriteArrayList创建一个线程同步的对象
private CopyOnWriteArrayList<Book> mBookList =
new CopyOnWriteArrayList<Book>();
//创建一个binder对象
private Binder mBinder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1,"服务端添加的书1"));
mBookList.add(new Book(2,"服务端添加的书2"));
}
//返回这个binder对象
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
服务端OnBind利用stub编写接口实现返回
7、 实现了parcelable的对象
package com.ryg.ipcaidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}