Android中的IPC方式——使用AIDL

来源:互联网 发布:心理学本科网络教育 编辑:程序博客网 时间:2024/06/10 16:47

上一篇博文介绍了使用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线程。




2 0
原创粉丝点击