Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

The principle of interprocess communication (IPC) was mentioned above, so let’s learn how to implement it.

🔥 Overview of Bound Services

A binding service is a server in a client-server interface. It allows components (such as activities) to bind to services, send requests, receive responses, and perform interprocess communication (IPC). A binding service typically exists only when it serves another application component and does not run in the background indefinitely.

💥 Basic Knowledge

A binding Service is an implementation of the Service class that allows other applications to bind to and interact with it. To provide bindings for the service, you must implement the onBind() callback method. This method returns an IBinder object that defines the programming interface the client can use to interact with the service.

🔥 Messenger

💥 overview

A lot of people think of IPC as AIDL, but if it’s just multiple processes and single threads, you can use Messenger to interface to your service.

Using Messenger is easier than using AIDL because Messenger queues all calls to the service.

For most applications, the service does not need to perform multithreading, so using Messenger allows the service to handle one call at a time. If multithreading is important in your service, then you need to use ALDL.

💥 Use the Messenger step

  • 1. The Service implements a Handler that receives a callback from each call made by the client.

  • The service uses Handler to create a Messenger object (which is a reference to Handler).

  • Messenger creates an IBinder that is returned to the client from onBind().

  • 4. The client uses IBinder to instantiate Messenger (the Handler that references the service), which the client uses to send Message objects to the service.

  • 5. The service receives each message in its Handler’s handleMessage().

💥 instance (Client to Server data transfer)

🌀 MessengerService. Java

public class MessengerService extends Service {
    public static final int MSG_SAY_HELLO = 0;
    // Let the client send a message to the IncomingHandler.
    Messenger messenger = null;

    // When bound to a service, we return an interface to our Messenger to send messages to the service.
    public IBinder onBind(Intent intent) {
        MLog.e("MessengerService:onBind");
        // Create a Messenger object (reference to Handler)
        messenger = new Messenger(new IncomingHander(this));
        // Return the IBinder that supports this Messenger.
        return messenger.getBinder();
    }
    // Implements a Handler
    static class  IncomingHander extends Handler {
        private Context appliacationContext;
        public IncomingHander(Context context) {
            appliacationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_SAY_HELLO:
                    Bundle bundle = msg.getData();
                    String string = bundle.getString("name");
                    // Process messages from clients
                    MLog.e("HandleMessage: From Acitvity."+string);
                    break;
                case 1:
                    
                    break;
                default:
                    super.handleMessage(msg); }}}}Copy the code

🌀 AndroidMainfest. XML

        <service android:name=".ipc.MessengerService"
            android:process="com.scc.ipc.messengerservice"
            android:exported="true"
            android:enabled="true"/>
Copy the code

Create different processes using the Android: Process property.

🌀 MainActivity. Class

public class MainActivity extends ActivityBase implements View.OnClickListener {
    Messenger mService = null;
    Messenger messenger = null;
    private boolean bound;
    private ViewStub v_stud;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); . } ServiceConnection connection =new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // Create a Messenger from the original IBinder that was previously retrieved using getBinder.
            mService = new Messenger(service);
            bound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bound = false; }};@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_bind_service:
                bindService(new Intent(MainActivity.this, MessengerService.class), connection, Context.BIND_AUTO_CREATE);
                break;
            case R.id.btn_send_msg:
                Message message = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
                Bundle bundle = new Bundle();
                bundle.putString("name"."Scc");
                message.setData(bundle);
                try {
                    mService.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break; }}@Override
    protected void onStop(a) {
        super.onStop();
        if (bound) {
            unbindService(connection);
            bound = false; }}}Copy the code

🌀 runs as follows

Two processes also exist and communicate with each other and pass data across.

💥 instance (Server passes data back to Client)

Not only do I want to pass the message to the Server, BUT I also want the Server to process the data and pass it to the Client.

🌀 MessengerService. Java

public class MessengerService extends Service {
    /** used to show and hide our notifications. * /
    ArrayList<Messenger> mClients = new ArrayList<Messenger>();
    /** Saves the last value set by the client. * /
    int mValue = 0;

    /** * add Messenger (from client) to array. The replyTo field of Message must be the Messenger of the client to which the callback should be sent. * /
    public static final int MSG_REGISTER_CLIENT = 1;

    /** * remove Messenger (from client) from array. The replyTo field of Message must be the Messenger of the client previously given with MSG_REGISTER_CLIENT. * /
    public static final int MSG_UNREGISTER_CLIENT = 2;
    /** * to set a new value. * This can be sent to the service to provide the new value, and will be sent by the service to any registered client with the new value. * /
    public static final int MSG_SET_VALUE = 3;
    // Let the client send a message to the IncomingHandler.
    Messenger messenger = null;

    // When bound to a service, we return an interface to our Messenger to send messages to the service.
    public IBinder onBind(Intent intent) {
        MLog.e("MessengerService-onBind");
        // Create a Messenger object (reference to Handler)
        messenger = new Messenger(new IncomingHander(this));
        // Return the IBinder that supports this Messenger.
        return messenger.getBinder();
    }
    // Implements a Handler
    class  IncomingHander extends Handler {
        private Context appliacationContext;
        public IncomingHander(Context context) {
            appliacationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_REGISTER_CLIENT:
                    mClients.add(msg.replyTo);
                    break;
                case MSG_UNREGISTER_CLIENT:
                    mClients.remove(msg.replyTo);
                    break;
                case MSG_SET_VALUE:
                    mValue = msg.arg1;
                    for (int i=mClients.size()-1; i>=0; i--) {
                        try {
                            mClients.get(i).send(Message.obtain(null,
                                    MSG_SET_VALUE, mValue, 0));
                        } catch (RemoteException e) {
                            // The client is gone. Remove it from the list;
                            // Safe from back to front, from front to back through the number of groups to cross the border.mClients.remove(i); }}default:
                    super.handleMessage(msg); }}}}Copy the code

🌀 MainActivity. Java

public class MainActivity extends ActivityBase implements View.OnClickListener {
    Messenger mService = null;
    Messenger messenger = null;
    private boolean bound;
    private ViewStub v_stud;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); . } ServiceConnection connection =new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // Create a Messenger from the original IBinder that was previously retrieved using getBinder.
            mService = new Messenger(service);
            bound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bound = false; }};static class ReturnHander extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MessengerService.MSG_SET_VALUE:
                    // I want to take off: handle here
                    MLog.e("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg); }}}@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_bind_service:
                bindService(new Intent(MainActivity.this, MessengerService.class), connection, Context.BIND_AUTO_CREATE);
                break;
            case R.id.btn_send_msg:
                try {
                    mMessenger = new Messenger(new ReturnHander());
                    Message msg = Message.obtain(null,
                            MessengerService.MSG_REGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    MSG. ReplyTo = mMessenger;
                    mService.send(msg);

                    // Give it some value as an example.
                    msg = Message.obtain(null,
                            MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
                    // The arg1 value passed in: this.hashcode ()
                    mService.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break; }}@Override
    protected void onStop(a) {
        super.onStop();
        if (bound) {
            unbindService(connection);
            bound = false; }}}Copy the code

🌀 runs as follows

We received a message from MesengerService in MainActivity handler.sendMessger ().

This is a simple Demo of how to communicate between Messenger processes. Finally let’s look at a wave of source code.

🔥 Messenger source

Messenger.java

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
    public IBinder getBinder(a) {
        returnmTarget.asBinder(); }...public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }}Copy the code

Then you’ll find that as long as the code is still in the IMessenger, let’s find it.

IMessenger.aidl

package android.os;

import android.os.Message;

/ * *@hide* /
oneway interface IMessenger {
    void send(in Message msg);
}
Copy the code

new Messenger(Handler handelr)

We’re actually calling getIMessenger() with Handler. Let’s go to handler. class.

    @UnsupportedAppUsage
    final IMessenger getIMessenger(a) {
        synchronized (mQueue) {
            if(mMessenger ! =null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            returnmMessenger; }}// Create the Messenger implementation class
    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            //Messenger calls the send() method to send a message through Handler.
            // The message is then received on the server via the Handler's handleMessge(MSG).
            Handler.this.sendMessage(msg); }}Copy the code

new Messenger(IBinder target)

package android.os;
/ * *@hide* /
public interface IMessenger extends android.os.IInterface
{
  /** Default implementation for IMessenger. */
  public static class Default implements android.os.IMessenger
  {
    @Override public void send(android.os.Message msg) throws android.os.RemoteException
    {}@Override
    public android.os.IBinder asBinder(a) {
      return null; }}/** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements android.os.IMessenger
  {
    /** Construct the stub at attach it to the interface. */
    public Stub(a)
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /** * Cast an IBinder object into an android.os.IMessenger interface, * generating a proxy if needed. */
    public static android.os.IMessenger asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      // Check whether it is in the same process.
      if(((iin! =null)&&(iin instanceof android.os.IMessenger))) {
        // Same process
        return ((android.os.IMessenger)iin);
      }
      // Proxy object
      return new android.os.IMessenger.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder(a)
    {
      return this; }... }public void send(android.os.Message msg) throws android.os.RemoteException;
}
Copy the code

If you look at the code above, you’ll see that it’s an AIDL, right? What is aiDL? We’ll continue in the next article.