Binder

Introduction

what is it

[1]Binder is an Android-specific interprocess communication mechanism, and remote method invocation system. It is one of the kernel modules of Android.

[2] IBinder is the base interface for a remotable object, the core part of a lightweight RPC (remote procedure call) mechanism designed for high performance when performing in-process and cross-process calls.

Shortly, Android offer an unique IPC mechanism which is Binder, Ibinder is the interface describes the abstract protocol for interacting with a remotable object.

Ibinder organization

[3] The following figure shows the Java layer of the Binder framework which contains main classes and their dependencies

Ibinder and Parcelable

[4] Parcel is a container for a message (data and object references) that can be sent through an IBinder.

[5] Parcelable is the Interface for classes whose instances can be written to and restored from a Parcel.

That means any class implements Parcelable can be passed through Ibinder, for example Intent.

Usability patterns

Implement IBinder allows you to perform in-process and cross-process calls.

Ibinder and inter process

The most flexible way to implement IBinder is using AIDL (Android Interface Definition Language).

AIDL generates code that takes care of writing the values into the parcels (marshalling) , sending them via Binder IPC, receiving them, reading the values (unmarshalling) and calling the methods of service and writing and sending the result back.

It is widely used in Android SDK, but it should be used carefully. [6] Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service.

Here is an example how to use AIDL pass callback to activity

  1. define an AIDL file
1
2
3
4
interface ICallback {
void onSuccess(in Account account);
void onError(int errorCode, String errorMessage);
}

\2. create a custom object class that supports the Parcelable which will be used for communication

  • Make your class implement the Parcelable interface.
  • Implement writeToParcel, which takes the current state of the object and writes it to a Parcel.
  • Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CallBack implements Parcelable {

public static final Creator<CallBack> CREATOR
= new Creator<CallBack>() {
@Override
public CallBack createFromParcel(Parcel in) {
return new CallBack(in);
}

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

private ICallBack mCallBack;

public CallBack(Parcel parcel) {
mCallBack = ICallBack.Stub.asInterface(parcel.readStrongBinder());
}

public void onSucess(Account account) {
mCallBack.onSuccess(account);
}

public void onError(int errorCode, String errorMessage) {
mCallBack.onError(errorCode, errorMessage);
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(mMyAccountRequestResponse.asBinder());
}
...
}

\3. start activity and pass this callback through intent

1
2
3
4
5
6
7
8
9
Callback callback = new CallBack(new ICallBack.Stub(){
@Override
public void onSuccess(Account account){
...
}
...
});
intent.putExtra(CALL_BACK, callback);
startActivity(intent);

Ibinder inside of service

Using Bound Service is the most common situation you need to implement Ibinder.

[7] Bound Service allows components (such as activities) to bind to the service, send requests, receive responses, and perform interprocess communication (IPC).

To provide binding for a service, you must implement the onBind() callback method. This method returns an IBinder object that defines the programming interface that clients can use to interact with the service.

According to different situations, you can choose different ways to implement this interface.

  1. If your service is private to your own application and runs in the same process as the client (which is common), you should create your interface by extending the Binder class and returning an instance of it from onBind().
  2. If you need your interface to work across different processes, you can create an interface for the service with a Messenger. (which is based on AIDL)
  3. If you want your service to handle multiple requests simultaneously, then you can use AIDL directly. In this case, your service must be thread-safe and capable of multi-threading.

Here is an example how to use bound service and extend binder to pass callback to activity

  1. create a custom service and define a binder which helps you get its instance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CustomService extends Service {
@NonNull
private final IBinder binder = new LocalBinder();

@NonNull
private Callback callback;

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

void setCallback(Callback callback){
this.callback=callback;
}

class LocalBinder extends Binder {

CustomService getService() {
return CustomService.this;
}

Callback getCallback(){
return this.callback;
}
}
}

\2. implemen ServiceConnection and the onServiceConnected() callback.

1
2
3
4
5
6
7
8
9
10
11
12
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
CustomService.LocalBinder binder
= (CustomService.LocalBinder) service;
final CustomService mService = binder.getService();
if (mService != null) {
mService.setCallback(callback);
}
}
};

\3. bind this service in your client (activity or ApplicationContext etc)

1
2
final Intent intent = new Intent(context, CustomService.class); 
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);

\4. you can use the same way to bind another client to this service and pass callback to this new client.

ResultReceiver Ibinder as simplified

If you just want to receive a callback result you can use ResultReceiver.

[8] ResultReceiver is a generic interface for receiving a callback result from someone. Use this by creating a subclass and implement onReceiveResult(int, Bundle), which you can then pass to others and send through IPC, and receive results they supply with send(int, Bundle).

It is also implemented by wrapping around Binder

Here is an example how to use ResultReceiver to receive a callback result, no need to pass callback to activity

  1. implement a ResultReceiver and its onReceiveResult callback and pass it by intent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
intent.putExtra(RESULT_RECEIVER, new ResultReceiver(null) {
@Override
protected void onReceiveResult(final int resultCode, final Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == RESULTOK) {
Account account = resultData.getParcelable(REQUESTEDACCOUNT);
if (account != null) {
listener.onSuccess(account);
} else {
listener.onFailure();
}
} else {
listener.onFailure();
}
}
});
context.startActivity(intent);

\2. in activity get this resultReceiver and call its send method

1
2
3
4
5
resultReceiver = intent.getParcelableExtra(RESULT_RECEIVER);
...
Bundle bundle = new Bundle();
bundle.putParcelable(REQUESTEDACCOUNT, account);
resultReceiver.send(RESULTOK, bundle);

Conclusion

Binder is Android IPC mechanism, it has a low level implementation, work as an important module in Android.

Android provides IBinder interface and Binder class as high level abstraction.

If you want to perform custom in-process call, you just need to implement IBinder interface.

If you want to define custom programming interface to realize your own IPC logic, you need to implement IBinder interface or extend Binder class and work with service.

For some simple situations, like receive a callback result, you can choose resultReceiver class which has already wrapped around a Binder.

Messenger(Ibinder) as callback channel

You can use Messenger with service like AIDL. Little difference is you need to implement a handler to handle message callback

  • server side
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MessengerService extends Service {
static final int MSG_USE_ACCOUNT = 1;

static class IncomingHandler extends Handler {
private Context applicationContext;

private Callback callback;
IncomingHandler(Context context, Callback callback) {
applicationContext = context.getApplicationContext();
this.callback = callback;
}

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USE_ACCOUNT:
callback.success((Account)msg.obg);
break;
default:
super.handleMessage(msg);
}
}
}

Messenger mMessenger;

@Override
public IBinder onBind(Intent intent) {
mMessenger = new Messenger(new IncomingHandler(this,callback));
return mMessenger.getBinder();
}
}
  • client side

Since messenger impements Parcelable and also provides implementation for Ibinder, you can also use it similar to ResultReceiver without service.

References

[1] https://elinux.org/Android_Binder

[2] https://developer.android.com/reference/android/os/IBinder

[3] https://www.nds.ruhr-uni-bochum.de/media/attachments/files/2012/03/binder.pdf

[4] https://developer.android.com/reference/android/os/Parcel

[5] https://developer.android.com/reference/android/os/Parcelable

[6] https://developer.android.com/guide/components/aidl

[7] https://developer.android.com/guide/components/bound-services

[8] https://developer.android.com/reference/android/os/ResultReceiver

http://gityuan.com/2015/11/28/binder-summary/

https://www.jianshu.com/p/2e6936e2de3d