Personal blog: www.milovetingting.cn

Manage AIDL with BinderPool

preface

When we use AIDL, the general process might look like this:

  • Define the AIDL interface

  • Define a service that returns an instance of the Stub class that implements the AIDL interface in the onBind method

  • Call the bindService method to retrieve the Binder object from the onServiceConnected callback of ServiceConnection

  • Invoke methods in the interface through Binder objects

The above approach works if we only use a small number of AIDL calls, but if we need to invoke a large number of AIDL calls, we can access a BinderPool, query the relevant Binder services from the BinderPool, and return the Binder services. With this unified management approach, AIDL can be easily managed.

The following describes the definition of BinderPool

Define IBinderPool

Define an IBinderPool, and the queryBinder method returns an IBinder object

interface IBinderPool {
    IBinder queryBinder(int queryCode);
}
Copy the code

Define BinderPool

Define BinderPool as a singleton. Get the instance of BinderPool by getInstance and connect to BinderPoolService in the constructor

private static class BinderPoolHolder {
        static final BinderPool INSTANCE = new BinderPool();
    }

public static BinderPool getInstance(Context context) {
        mContext = context.getApplicationContext();
        return BinderPoolHolder.INSTANCE;
    }

private BinderPool(a) {
        connectBinderPoolService();
    }

private synchronized void connectBinderPoolService(a) {
        countDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        try {
            countDownLatch.await();
        } catch(InterruptedException e) { e.printStackTrace(); }}Copy the code

BinderPoolService returns the IBinderPool object

public class BinderPoolService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return newBinderPool.BinderPoolImpl(); }}Copy the code

BinderPoolImpl implements IBinderPool interface methods, and queryBinder returns the corresponding Binder according to queryCode

static class BinderPoolImpl extends IBinderPool.Stub {

        @Override
        public IBinder queryBinder(int queryCode) throws RemoteException {
            IBinder binder = null;
            switch (queryCode) {
                case QUERY_CODE_BINDER_A:
                    binder = new BinderA();
                    break;
                case QUERY_CODE_BINDER_B:
                    binder = new BinderB();
                    break;
                default:
                    break;
            }
            returnbinder; }}Copy the code

ServiceConnection returns the Binder object in onServiceConnected and binds DeathRecipient

private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binderPool = IBinderPool.Stub.asInterface(service);
            try {
                binderPool.asBinder().linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {}};Copy the code

DeathRecipient

private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied(a) {
            binderPool.asBinder().unlinkToDeath(deathRecipient, 0);
            binderPool = null; connectBinderPoolService(); }};Copy the code

Complete BinderPool code

public class BinderPool {

    public static final int QUERY_CODE_BINDER_A = 1;

    public static final int QUERY_CODE_BINDER_B = 2;

    private static Context mContext;

    private CountDownLatch countDownLatch;

    private IBinderPool binderPool;

    public static BinderPool getInstance(Context context) {
        mContext = context.getApplicationContext();
        return BinderPoolHolder.INSTANCE;
    }

    private BinderPool(a) {
        connectBinderPoolService();
    }

    private synchronized void connectBinderPoolService(a) {
        countDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        try {
            countDownLatch.await();
        } catch(InterruptedException e) { e.printStackTrace(); }}public IBinder queryBinder(int queryCode) {
        IBinder binder = null;
        if(binderPool ! =null) {
            try {
                binder = binderPool.queryBinder(queryCode);
            } catch(RemoteException e) { e.printStackTrace(); }}return binder;
    }

    private static class BinderPoolHolder {
        static final BinderPool INSTANCE = new BinderPool();
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binderPool = IBinderPool.Stub.asInterface(service);
            try {
                binderPool.asBinder().linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {}};private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied(a) {
            binderPool.asBinder().unlinkToDeath(deathRecipient, 0);
            binderPool = null; connectBinderPoolService(); }};static class BinderPoolImpl extends IBinderPool.Stub {

        @Override
        public IBinder queryBinder(int queryCode) throws RemoteException {
            IBinder binder = null;
            switch (queryCode) {
                case QUERY_CODE_BINDER_A:
                    binder = new BinderA();
                    break;
                case QUERY_CODE_BINDER_B:
                    binder = new BinderB();
                    break;
                default:
                    break;
            }
            returnbinder; }}}Copy the code

Define IBinderA and its implementation classes

interface IBinderA {
    int calc(int a,int b);
}
Copy the code
public class BinderA extends IBinderA.Stub {
    @Override
    public int calc(int a, int b) throws RemoteException {
        returna + b; }}Copy the code

Define IBinderB and implementation classes

interface IBinderB {
    String getInfo(String info);
}
Copy the code
public class BinderB extends IBinderB.Stub {
    @Override
    public String getInfo(String info) throws RemoteException {
        return info + "-handled"; }}Copy the code

The client calls BinderPool

Since connecting to BinderPoolService is done by calling countdownlatch.await (), waiting for a callback from onServiceConnected, calling BinderPool’s method directly from the main thread will block the main thread and cause ANR. So you need to put it into a child thread.

new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    IBinder binder = BinderPool.getInstance(getApplicationContext()).queryBinder(BinderPool.QUERY_CODE_BINDER_A);
                    final int calc = IBinderA.Stub.asInterface(binder).calc(1.2);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run(a) {
                            Toast.makeText(getApplicationContext(), "calc:"+ calc, Toast.LENGTH_SHORT).show(); }}); IBinder binderB = BinderPool.getInstance(getApplicationContext()).queryBinder(BinderPool.QUERY_CODE_BINDER_B);final String info = IBinderB.Stub.asInterface(binderB).getInfo("hello");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run(a) {
                            Toast.makeText(getApplicationContext(), "info:"+ info, Toast.LENGTH_SHORT).show(); }}); }catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }).start();
Copy the code

conclusion

BinderPool is defined to manage all AIDL connections. When adding AIDL, add the corresponding AIDL interface and corresponding implementation Binder, and add the corresponding queryCode processing to The queryBinder of BinderPool.