AIDL使用及Binder连接池

1. AIDL使用

  Demo详细地址

  AIDL是Android中一种特有的进程间通信手段,看完AIDL的使用后,感觉和当前比较流行的RPC框架比较相似。RPC也需要使用接口定义语言去声明接口对象和接口定义。以便生成相应的远程调用代码。

    假设有这么一个场景,进程A提供了两个服务:

    a. 查询图书列表

    b. 添加图书

    1. 我们首先需要声明一个图书类,由于这个图书类Book的对象需要在两个进程之前传递,所以我们需要实现Android中特有的序列化接口Parcelable。

package com.alipay.aidl.test;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by tianbei on 2016/1/31.
 */
public class Book implements Parcelable {

    public int bookId;

    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    public Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }

    };
}

    2. 定义aidl文件

    对于aidl接口中要使用的自定义类,必须要在aidl文件中进行声明。下面是Book.aidl。

package com.alipay.aidl.test;

parcelable Book;
       图书管理接口aidl文件

// IBookManager.aidl
package com.alipay.aidl.test;

import com.alipay.aidl.test.Book;
import com.alipay.aidl.test.IOnNewBookArrivedListener;

// Declare any non-default types here with import statements

interface IBookManager {

    List<Book> getBookList();

    void addBook(in Book book);

    void registerListener(in IOnNewBookArrivedListener listener);

    void unregisterListener(in IOnNewBookArrivedListener listener);

}
     图书订阅消息推送接口:

// IOnNewBookArrivedListener.aidl
package com.alipay.aidl.test;
import com.alipay.aidl.test.Book;

// Declare any non-default types here with import statements

interface IOnNewBookArrivedListener {

    void onNewBookArrived(in Book book);

}
    上面的代码需要在客户端进程和服务端进程中同时存在。因为客户端和服务端都需要使用到IBookManager接口和Book类。

     服务端代码需要实现IBookManager.Stub接口,以执行具体的业务逻辑。比如添加图书:服务端需要将图书保存起来,可以保存到数据库持久化,这就是服务端的业务逻辑。在实现具体的业务逻辑之前,我们先看下Android studio为我们生成的接口文件IBookManager.java。这个Java类看起来很复杂,其实结构比较简单,它继承了类IInterface。

    a. 接口方法定义,比如我们在aidl文件中声明的addBook方法,getBookList方法。

    b. 内部类Stub。这个类集成了IBinder,它就是我们所说的Binder类,运行在服务端。服务端代码通过集成这个类会进行具体的业务逻辑。

    c. Proxy类:运行于客户端,跟我们些http通讯时的逻辑一样,它会把入参序列化,然后远程调用,然后将返回的结果反序列化。

    接下来,我们看下服务端的代码:

package com.alipay.aidl.test;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

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

/**
 * Created by tianbei on 2016/1/31.
 */
public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private List<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
            new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            onNewBookArrived(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.beginBroadcast();
            boolean result = mListenerList.register(listener);
            Log.d(TAG, "register listener :" + result);
            mListenerList.finishBroadcast();
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.beginBroadcast();
            boolean result = mListenerList.unregister(listener);
            Log.d(TAG, "unregister listener :" + result);
            mListenerList.finishBroadcast();
        }
    };

    public void onNewBookArrived(Book book) {
        mBookList.add(book);
        int N = mListenerList.beginBroadcast();
        for(int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
            try {
                listener.onNewBookArrived(book);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        mListenerList.finishBroadcast();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "ARM体系结构与编程"));
        mBookList.add(new Book(2, "Android开发艺术探索"));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

    BookManagerService是运行在服务端的一个Service类,它里面有一个内部匿名类,实现了IBookManager.Stub接口,这就是我们上面说的运行于服务端的代码,它会实现aidl接口定义文件中声明的四个方法。添加图书、获取图书列表、添加图书时,调用注册到Manager中的各个listener通知其他进程有新书上架。

2. Binder连接池

     为什么需要Binder线程池呢?按照上面的写法,服务器端进程每声明一个Binder接口供客户端进程调用,就需要声明一个Service组件。而如果一个客户端端有100个Binder类的话,就需要定义100个Service组件,并且需要在Manifest文件中声明。这样是很消耗系统资源的

     在上面的实现中,我们可以发现,服务端返回Binder类到客户端的方法是:在onBind的实现中返回一个IBookManager.Stub对象。

     那么我们可不可以定义一个统一的Service,然后客户端进程传一个标识进来,服务端进程通过不同的标识返回不同的Binder对象呢?答案是可以的。

      这里就不详细说了,直接上Demo。Demo详细地址



 

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值