Binder通信使用实例

1、Binder基本概念

系统内部有一个Binder服务,相当于DNS服务器,用于分发客户端请求,并将请求发送到服务端处理,再将结果返回客户端。

2、为什么Binder进行IPC通讯会高效

普通IPC方式需要经过序列化、反序列化,会有两次内存拷贝,Binder机制内部有共享内存的概念,做一次内存拷贝即可

3、实现方式

(1)AIDL方式

注意点:AIDL文件在Server端创建,要讲生成的 IBinderXXX.java、数据实体类 文件复制到客户端

(2)自行实现IBinderXXX源码

两步,第一步定义IInterface接口

public interface IBookManager extends IInterface {

    static final String DESCRIPTOR = "com.ideacode.study.binderserver.IBookManager";

    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

    public List getBookList() throws RemoteException;
    public void addBook(Book book) throws RemoteException;

}

第二步编写Binder实现类,Proxy用于提供给客户端使用

public class BookManagerImpl extends Binder implements IBookManager {

    public BookManagerImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IBookManager asInterface(IBinder obj) {
        if (obj ==null) {
            return null;
        }

        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin != null && (iin instanceof IBookManager)) {
            return (IBookManager)iin;
        }
        return new Proxy(obj);
    }

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
            throws RemoteException {

        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;
            case TRANSACTION_getBookList:
                data.enforceInterface(DESCRIPTOR);
                List result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
            case TRANSACTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0;
                if (0 != data.readInt()) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                } else {
                    arg0 = null;
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true;
        }

        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public List getBookList() throws RemoteException {
        return null;
    }

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

    }

    private static class Proxy implements IBookManager {

        private IBinder mRemote;

        Proxy(IBinder remote) {
            mRemote = remote;
        }

        @Override
        public List getBookList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Book.CREATOR);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if (book != null) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addBook, data, reply, 0);
                reply.readException();
            } finally {
                reply.recycle();
                data.recycle();
            }
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

    }

    @Override
    public IBinder asBinder() {
        return this;
    }

}

4、线程调度

客户端在主线程同步执行,服务器端在Binder线程响应请求


截屏2022-03-20 下午5.12.28.png

5、服务器端断开后自动重连

在客户端设置DeathRecipient连接监听,监听到服务器端断开后,再次执行重连操作。或者服务器端自定监听自己的服务是否死亡,自行维护服务重启。

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.e(TAG, "服务器连接已死亡");
            if (bookManager == null) {
                return;
            }

            bookManager.asBinder().unlinkToDeath(deathRecipient, 0);
            bookManager = null;
            startToBindService();
        }
    };

6、一些坑

(1)客户端bindService绑定服务器端服务失败
Android 11 后,要在客户端MainiFest中设置参数,才能通过ComponentName启动另一个APP的服务;
服务端服务要设置action才能被客户端搜索到

客户端设置

    
        
    

    private void startToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.ideacode.study.binderserver.service.BookManagerCustomBinderService");
        intent.setPackage("com.ideacode.study.binderserver");

        bindService(intent, connection, Service.BIND_AUTO_CREATE);
    }
服务器端设置
        
            
                
            
        

(2)实测 一加9 Android 12 真机,服务端APP未启动的情况下,客户端APP无法启动服务端APP的服务。模拟器正常

你可能感兴趣的:(Binder通信使用实例)