Android中的IPC方式——使用AIDL

上一篇博文介绍了使用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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值