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.