您的位置:首页 > 移动开发 > Android开发

Android中的IPC方式——使用AIDL

2016-06-17 15:47 666 查看
上一篇博文介绍了使用Messenger来进行IPC,可以发现Messenger是以串行的方式处理客户端发来的消息,如果有大量的消息发过来只能一个一个的处理,就先的不太合适。这篇博文介绍使用AIDL进行进程间通信,使用IPC。

AIDL的大概实现过程如下

1、服务端

服务端首先要创建一个Service用来坚挺客户端的链接请求,然后创建一个AIDL文件,将暴露给客户端的接口在合格AIDL文件中声明,然后在Service中实现这个AIDL接口即可。

2、客户端

客户端所要做的事情就稍微简单点,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着调用AIDL中的方法就可以了。

工程目录:



首先创建一个IBookManager.aidl

package com.qian.aidlipc;

import com.qian.aidlipc.Book;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}

在aidl文件中声明了两个方法,这两个方法就是暴露给客户端的,我们需要服务端实现。另外上面的aidl文件还用到了Book这个类,所以也要创建Book.aidl,然后添加如下内容

package com.qian.aidlipc;

parcelable Book;

Book.java是一个实体类,继承了Parcelable,可序列化

package com.qian.aidlipc;

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 Parcelable.Creator<Book> CREATOR = new Parcelable.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);
}

}

3、远程服务端Service的实现

上面定义了AIDL接口,接下来要再服务端实现这个AIDL接口,先创建一个BookManagerService,代码如下:

package com.qian.aidlipc;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;

public class BookManagerService extends Service {

private static final String TAG = "BookManagerService";

private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
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, "Android"));
mBookList.add(new Book(2, "Ios"));
}

@Override
public IBinder onBind(Intent intent) {

return mBinder;
}
}

上面是一个服务端Service的典型实现,首先在onCreate中中石化添加了两本图书的信息,然后创建了一个Binder对象并在onBind方法中返回它,这个对象继承自IBookManager.Stub对象,并实现了它内部的AIDL方法,这个过程在浅谈Binder机制一文中已经详细说明了,这里不多说,也就是前面所说的,将暴露给客户端的接口在Service中实现。其中实现了getBookList,addBook两个方法,实现过程也比较简单,只是为了说明原理,不用搞那么复杂,实际情况中可能会出现很复杂的情况,但是原理都一样。注意这里采用CopyOnWriteArrayList,这个CopyOnWriteArrayList支持并发读写。实际上AIDL方法是在服务端的Binder线程池中执行的,这样就有多有客户端同时访问的情况,所以需要处理线程同步问题,而这个CopyOnWriteArrayList就可以处理线程同步。

然后注册一下远程服务:

<service
android:name="com.qian.aidlipc.BookManagerService"
android:process=":remote" >
</service>

4、客户端的实现

客户端的实现,首先要绑定服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后通过这个接口去调用服务端的远程方法,代码如下:

package com.qian.aidlipc;

import java.util.List;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.qian.aidlipc.R;

public class BookManagerActivity extends Activity {

private static final String TAG = "BookManagerActivity";
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);

try {
List<Book> list = bookManager.getBookList();

Log.i(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "夜莺与玫瑰");
bookManager.addBook(newBook);
Log.i(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list:" + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}

public void onServiceDisconnected(ComponentName className) {

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}

}
代码很简单,其中IBookManager bookManager = IBookManager.Stub.asInterface(service);就是将服务端返回的Binder对象转换成AIDL接口,再通过这个接口调用远程方法。注册xml然后观察一下Log:



先是getBookList。得到两本书  Android和IOS,然后调用addBook添加了一本书夜莺与玫瑰,然后又调用getBookList。和我们预想的结果是一样的,远程调用方法成功了。

最后再说明一下:

当客户端远程调用服务的方法时,被调用的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,这个时候如果服务端的方法比较耗时,就会出现客户端线程尝试加被阻塞,而如果这个客户端是UI线程的话,就会出现NR,客户端的onServiceConnected和onServiceDisconnected都是运行在UI线程,所以尽量避免UI线程调用远程方法。而服务端的方法本身就运行在Binder线程池里,所以本身可以执行大量耗时操作,所以不必再开线程执行耗时操作。另外服务端也有可能会运行客户端的回调方法,而如果客户端的这个回调方法比较耗时的话,也会造成服务端ANR,所以最好保证服务端回调接口的地方运行在非UI线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: