1

From https://developer.android.com/guide/components/bound-services.html:

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(). The client receives the Binder and can use it to directly access public methods available in either the Binder implementation or the Service.

This is the preferred technique when your service is merely a background worker for your own application. The only reason you would not create your interface this way is because your service is used by other applications or across separate processes.

If you need your interface to work across different processes, you can create an interface for the service with a Messenger...

This seems to imply you wouldn't use Binder for inter-process communication, but IBinder documentation says the opposite:

Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend from Binder.

The key IBinder API is transact() matched by Binder.onTransact(). These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to transact() does not return until the target has returned from Binder.onTransact(); this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.

And Binder documentation:

You can, however, derive directly from Binder to implement your own custom RPC protocol or simply instantiate a raw Binder object directly to use as a token that can be shared across processes.

This class is just a basic IPC primitive...

Many other pages also mention binders in IPC context. What am I misunderstanding?

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487

2 Answers2

2

IBinder provides base interface for remotable object. Binder is an implementation of IBinder that provides the standard support creating a local implementation of remotable an object.

When both services and clients are running in same process, simply extending Binder and returning instance of the subclass of Binder is enough to communicate with service by binding to it as Binder is an implementation of IBinder that provides the standard support creating a local implementation of remotable an object.

When service is running in separate process and service's client can be running in any other process, we need to implement IBinder and provide implementation that provides support for remote implementation of remotable object. This can be achieved in one of two ways:

Using AIDL

Using AIDL, we can write an interface which we would like to expose. aidl tool then can parse this file and auto-generate Stub and some boiler plate code which is common for all apps for IPC. We can then extend Interface.Stub to provide implementation on service side.

Clients can include this AIDL file in their projects and code for IPC is auto-generated by aidl tool. Now, client can bind to your service and communicate using the interface declared in the AIDL file.

Using Messenger

Messenger is a reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by creating a Messenger pointing to a Handler in one process, and handing that Message to another process. This IPC is internally implemented using AIDL itself. Here is the IMessenger.aidl interface which Messenger uses internally to expose contract that clients can use to communicate to host process.

Summary

IBinder can be used for both in-process and inter-process communication. When using it for inter-process communication, just expose the interface using AIDL.

Ashish Pathak
  • 686
  • 4
  • 16
  • I beg to differ a bit. Messenger is doing something more than just an AIDL interface. Proof: Messenger can be used to communicate between activities in different process. I cant acheive this with raw binders. –  Mar 20 '20 at 14:59
1

I have been having problems with IPC for days. But with the charitable aid of pskink I learnt alot.

For one I hate AIDL. I hate the term. Why this strange new jargon when it really is just some java classes that implement onTransact and call transact. It is not necessary, and harder to implement than if you do it yourself!

So call AIDL interface:

Print.aidl

interface Print 
{
  void print(String s);
}

Forgive my aidl code. AIDL compiler however compiles it to a java file.

Print.java

// note that AIDL takes care to use fully qualified names
public interface Print
{
     void print(String s);

     // This is used for local process calls
     public abstract static class Stub extends Binder
                                                               implements Print
     {
          private Stub()
          {
            attachInterface(this);
          }

          public static Print asInterface(IBinder binder)
          {
           return binder == null ? null :
                      binder instanceof Print.Stub ? new Print.Stub() :
                        /* else */           new Print.Stub.Proxy(binder);
          }

          @Override public IBinder asBinder { return this; }

          @Override public boolean onTransact(int event,Parcel out,Parcel reply,int flags)
          {
               switch(event)
               {
                 case TRANSACTION_print: // print data.readString();
               }
          }

          // This is used for IPC
          private static class Proxy implements Print
          {
               IBinder binder;

               @Override public void print(String s)
               {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                data.writeString(s);
                binder.transact(TRANSACTION_print,data,reply,0);
                reply.readException();
               }
          }
     }
}

That is pretty much the code that AIDL generates and never shows you. I dont know why it is different from JIDL or PIEL or TBHJ. However we dont need that AIDL tool and all that code above can be simplified!

process1/Print.java

public class Print extends Binder
{
     public void print(String s) { // print string }

          @Override public boolean onTransact(int event,Parcel out,Parcel reply,int flags)
          {
               switch(event)
               {
                 case TRANSACTION_print: // print data.readString();
               }
          }
}

process2/Print

public class Print
{
     public void print(String s)
     {
      Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                data.writeString(s);
                binder.transact(TRANSACTION_print,data,reply,0);
                reply.readException();
     }
}

As you can see the simplicity. Only thing left out was the decision to use local functions versus process proxy. Which is easily implemented.

Note that Binders are not for IPC fullstop... They only work for bound Service to client communication. Trying to transact between two Activities doeant seem to work. As pointed out by pskink.

To communicate between two activities you can use the Messenger api. Messenger is said to just be using binders but I think that is half truth. As I stated, using a binder to communicate between two activities fail! Binder by the way is a kernel driver. So I guess the binder Messenger uses is a special kernel binder driver!

Another way to communicate is via ParcelFileDescriptor.createPipe()!!!! Yah baby. Android supports pipes. Problem is, marshalling them via intents is a dead fail and app crashes badly. Pipes again, seem to only be made for service to activity communication.

In the end. What a ridiculous thing not being able to use binders to chat up.two activities in different processes. Not being able to put a parcelable descriptor in an intent. You must just follow the doctrines set upon you. And to make matters worse, Poor documentations.