IPC之AIDL

来源:互联网 发布:手机淘宝直通车在哪 编辑:程序博客网 时间:2024/06/05 18:30

Binder

Binder是Android中的一个类,继承了IBinder接口,是一种跨进程通信方式。
从Framework角度看:Binder是ServiceManager连接各种Manager(ActivityManager WindowManager等)和相应的ManagerService的桥梁;当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过该对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

通过Service利用AIDL进行通信。

AIDL(Android Interface Definition Language)是一种接口定义语言,由于Android的每个进程都运行在独立的虚拟机中,所以进程之间通信会比较麻烦。我们可以利用AIDL将一个进程的数据拆分成Android系统可识别的数据单元,然后系统再重新将数据单元合成传递给另一个进程。这样就实现了进程间的通信。
实现功能:服务端有一个书库,客户端可以向服务端添加书本、查阅书目;服务端可以向注册了监听的客户端推送新书(客户端可以随时解除注册的监听)。在此基础上,服务端还会对客户端进行权限检查

创建 Book类实现Parcelable接口

只有实现了该接口才能在不同的进程间传递

创建Book.aidl

用于声明Book类

package cqupt.second.aidl;parcelable Book;

创建IOnNewBookArrivedListener.aidl

让客户端实现监听的接口

package cqupt.second.aidl;import cqupt.second.aidl.Book;interface IOnNewBookArrivedListener {    void onNewBookArrived(in Book newBook);}

创建IBookManager.aidl文件

暴露服务端的方法给客户端,暴露客户端的监听给服务端
package cqupt.second.aidl;import cqupt.second.aidl.Book;import cqupt.second.aidl.IOnNewBookArrivedListener;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);    void registerListener(IOnNewBookArrivedListener listener);    void unregisterListener(IOnNewBookArrivedListener listener);}

服务端的实现代码:

  • 使用CopyOnWriteArrayList保存Book是因为其支持并发的读写
  • RemoteCallbackList专门提供用于删除进程间listener的接口
  • 权限检查:checkCallingOrSelfPermission
    • 服务端和客户端如果是两个工程,则在Service的onBind方法中无法验证客户端的权限。原因是onBind方法并不是一个Binder调用,它运行在服务端的UI线程中,故在onBind中只能验证服务端的权限,然而这是没有意义的。推荐在onTransact方法中对客户端进行权限验证。
public class BookManagerServicec extends Service {    private static final String TAG = "BookManagerServicec";    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();    private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();    private AtomicBoolean isServicceDestoryed = new AtomicBoolean(false);    @Nullable    @Override    public IBinder onBind(Intent intent) {        //权限的检查,此处的不通过,直接不能完成绑定        int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");        Log.d(TAG, "onbind check=" + check);        if (check == PackageManager.PERMISSION_DENIED) {            return null;        }        return mBinder;    }    @Override    public void onCreate() {        super.onCreate();        mBookList.add(new Book(1, "Android"));        mBookList.add(new Book(2, "Ios"));        mBookList.add(new Book(3, "Java"));        new Thread(new ServiceWorker()).start();    }    //Binder需要实现AIDL文件中的接口的方法,以便该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 registerListener(IOnNewBookArrivedListener listener) throws RemoteException {            mListeners.register(listener);        }        //解除注册监听        @Override        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {            mListeners.unregister(listener);        }          //设置权限的第二种方式。此处权限检测失败不会执行AIDL中的方法        @Override        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");            if (check == PackageManager.PERMISSION_DENIED) {                return false;            }            String packageName = null;            String[] packagesForUid = getPackageManager().getPackagesForUid(getCallingUid());            if (packagesForUid != null && packagesForUid.length > 0) {                packageName = packagesForUid[0];            }            if (!packageName.startsWith("cqupt.second")) {                return false;            }            return super.onTransact(code, data, reply, flags);        }    };    //把新书发送给注册了监听的客户端    private void onNewBookArrived(Book book) throws RemoteException {        mBookList.add(book);        //注意RemoteCallbackList的使用方式        int i = mListeners.beginBroadcast();        for (int j = 0; j < i; j++) {            IOnNewBookArrivedListener item = mListeners.getBroadcastItem(j);            if (item != null) {                item.onNewBookArrived(book);            }        }        mListeners.finishBroadcast();    }    //服务端模拟自动增加新书    private class ServiceWorker implements Runnable{        @Override        public void run() {            while (!isServicceDestoryed.get()) {                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                int bookId = mBookList.size() + 1;                Book newBook = new Book(bookId, "new book#" + bookId);                try {                    //执行通知客户端的方法                    onNewBookArrived(newBook);                } catch (RemoteException e) {                    e.printStackTrace();                }            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        isServicceDestoryed.set(true);    }}
客户端代码实现

public class BookManagerActivity extends AppCompatActivity {    private static final String TAG = "BookManagerActivity";    private IBookManager remoteBookManager;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_book_manager);        //在此方法中绑定远程服务        Intent intent = new Intent(this, BookManagerServicec.class);        bindService(intent, mConnection, BIND_AUTO_CREATE);    }    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //通过IBookManager.Stub.asInterface(service)方法将服务器返回的Binder转换成AIDL接口            IBookManager bookManager = IBookManager.Stub.asInterface(service);            //另一个AIDL接口            remoteBookManager = bookManager;            try {                //设置Binder死亡监听                remoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);                List<Book> list = bookManager.getBookList();                Log.d(TAG, "query book list,list type:" + list.getClass().getCanonicalName());                Log.d(TAG, "query book list:" + list.toString());                Book book = new Book(4, "Android 开发艺术之旅");                bookManager.addBook(book);                List<Book> newList = bookManager.getBookList();                Log.d(TAG, newList.toString());                //客户端注册监听到远程服务端                bookManager.registerListener(mOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            remoteBookManager = null;        }    };    //服务端有新书更新时回调客户端的此方法,该方法在客户端的Binder线程池中执行,需要更新UI则需要Handler    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {        @Override        public void onNewBookArrived(Book newBook) throws RemoteException {            Message message = new Message();            message.what = 1;            message.obj = newBook;            mHandler.sendMessage(message);        }    };    Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 1:                    Log.d(TAG, "receive new book:" + msg.obj);                    break;            }            super.handleMessage(msg);        }    };       //设置死亡Binder死亡监听,如果死亡,重新绑定    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());            if (remoteBookManager == null)                return;            remoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);            remoteBookManager = null;            // TODO:这里重新绑定远程Service        }    };    @Override    protected void onDestroy() {        if (remoteBookManager != null && remoteBookManager.asBinder().isBinderAlive()) {            try {                remoteBookManager.unregisterListener(mOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        unbindService(mConnection);        super.onDestroy();    }}
权限设置

<permission        android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"        android:protectionLevel="normal" /><uses-permission android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"/>

系统会自动生成IBookManager.java文件,文件中的大致内容

  • DESCRIPTOR:Binder的唯一标识
  • asInterface:将服务端的Binder对象转换成客户端所需要的AIDL接口类型对象
    • 服务端与客户端在同一进程:返回服务端的Stub对象本身
    • 服务端与客户端不在同一进程:但会系统封装之后的Stub.proxy
  • asBinder:返回当前Binder对象
  • onTransact:运行在服务端的Binder线程池中,处理客户端的请求
    • 方法原型: onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)
      • code:服务端用于确定客户端请求的哪一个方法
      • data:目标方法所需参数
      • reply:目标方法的返回值
  • Proxy类中的getBookList():运行在客户端,内部实现如下:
    • 创建该方法所需要的输入型对象、输出型对象、返回值对象
    • 将该方法的参数写入输输入型对象
    • 调用transact 方法发起远程过程调用RPC,同时当前线程被挂起
    • 服务端调用onTransact 方法直到RPC结束,当前线程继续执行
    • 从输出型对象中取出RPC过程的结果
    • 返回输出型对象中的数据