Android

Understanding android Messenger and AIDL of Concurrency API

Now we got one more day to learn something new about the Service, Ok let’s make today something awesome to learn more about the service of concurrency API. In my last article, we understand about the Concurrency API of Service and Intent Service and Bound Service. In this article, we will learn about service which uses the technique of Messenger and AIDL. Wow Sounds great :).

Bound Service (Messenger and AIDL)

In this article, I am focusing on the bound services which are running in the background in the different process in the single thread and multi-thread environment. If we talk about the Single thread for sending the message in a queue the first thing comes to mind is that Messenger.

Messenger

Messenger will process the message in a different process by using IPC (Inter Process Communication) remote technique to communicate with the client and server. Messenger will be coupled with the handler, hence all the task will be in queue process in a Single thread by Handler.

For bind the service what you need to do with pass the Messenger instance reference in onBind() method and pass the reference of handler while creating the Messenger instance. Hereabouts message will be carried in the format of the bundle with Messenger that it is awesome. Let’s see for example to create one service which runs in the different process by using Messenger which is coupled by Handler.

public class MyServiceIPC extends Service {

    public static final int JOB_1 = 1;
    public static final int JOB_RESPONSE_1 = 2;

    Messenger messenger = new Messenger(new IncomingHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {

            Message message;
            Bundle bundle = new Bundle();
            String messageText;

            switch (msg.what) {
                case JOB_1:
                    messageText = msg.getData().getString("message");
                    message = Message.obtain(null, JOB_RESPONSE_1);
                    Toast.makeText(getApplicationContext(),messageText , Toast.LENGTH_SHORT).show();
                    bundle.putString("message_res", messageText.toUpperCase());
                    message.setData(bundle);
                    Messenger activityMessenger = msg.replyTo;
                    try {
                        activityMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }
}

Now let’s create activity class to bind this service to send the message to this server and again send back this response from server to client by using Messenger process by the handler.

public class BindServiceIPCActivity extends AppCompatActivity {

    private Button btnSend;
    private EditText editText;
    private Messenger messenger;
    private boolean isBound;
    private final Messenger mActivityMessenger = new Messenger(
            new ResponseTakeHandler(this));


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind_ipc);

        editText = (EditText)findViewById(R.id.messageEditText);
        btnSend = (Button)findViewById(R.id.sendButton);
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String messageText = editText.getText().toString();
                if (messageText.isEmpty()){
                    Toast.makeText(BindServiceIPCActivity.this, "Please enter message", Toast.LENGTH_LONG).show();
                }else{
                    Message message = Message.obtain(null,MyServiceIPC.JOB_1);
                    Bundle bundle = new Bundle();
                    bundle.putString("message", messageText);
                    message.setData(bundle);
                    message.replyTo = mActivityMessenger;
                    try {
                        messenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!isBound) {
            // start service here
            Intent intent = new Intent(BindServiceIPCActivity.this, MyServiceIPC.class);
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        isBound = false;
        messenger = null;
    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            messenger = null;

        }
    };


    public class ResponseTakeHandler extends Handler{

        public ResponseTakeHandler(Context context){

        }
        @Override
        public void handleMessage(Message msg) {

            switch (msg.what){
                case MyServiceIPC.JOB_RESPONSE_1:
                    String result = msg.getData().getString("message_res");
                    Toast.makeText(BindServiceIPCActivity.this, "Response: "+result, Toast.LENGTH_LONG).show();
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }

}

Here also required the Service Connection to build the connection between the client and the server. The handler will process the response of Messenger and update the user interface. But one more thing does not forget to add this service in AndroidManifest.xml.

<service android:name=".bindservice.ipc.MyServiceIPC" android:process=":remote"/>

Wow, Messenger is awesome to communicate with server and client. Let’s check how is AIDL works in a different process.

AIDL

AIDL means android interface definition language. It can process any task remotely by using IPC technique. Android has a strong mechanism to communicate between service and client through IPC technique. AIDL process the data between clients remotely and safely only thing is required that client should be bind to these services. AIDL support to process the generic data by using the Parcelable. Data processing through Parcelable is fast in the IPC mechanism. Different process means I am talking about communication between two different applications.

Let’s take a simple word to define AIDL IPC mechanism, suppose you have App1 consider as a remote server that running AIDL service. What does this service? it gets the list of users information of table and process data to the client or any business logic that get information from the client and do some task and give a response back to the client (It means App2). Here the user model should be Parcelable for fast communication.

I think it’s a big complex example, Let’s give another simple example to explain. I want to multiply by given two numbers. I will pass two different numbers as input and will tell to AIDL service to give the result as a response back to me.  It is simple task here but it can take any long background task to the process, for example, downloading any audio or video files from the server.

For communication with two different apps or process, I need to create the AIDL file in the same package of both apps. Why AIDL is required on the client side also it will create the copy of proxy for remote access.

Let’s create two different android studio project for App1 and App2.

// MultiplyNumberAidl.aidl
package com.example.testapp;

// Declare any non-default types here with import statements

interface MultiplyNumberAidl {

    int multiply(in int firstNumber, in int secondNumber);
}
public class AidlService extends Service{

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MultiplyNumberAidl.Stub() {
            @Override
            public int multiply(int firstNumber, int secondNumber) throws RemoteException {
                return (firstNumber * secondNumber);
            }
        };
    }
}

You need to set the build and sync to access the aidl file.

 sourceSets {
        main {
            aidl.srcDirs = ['src/main/aidl']
        }
    }

Add the service in AndroidManifest.xml file.

<service android:name=".bindservice.ipc.aidl.AidlService" android:process=":remote">
            <intent-filter>
             <action android:name="com.example.testapp.bindservice.ipc.aidl.MultiplyNumberAidl" />
           </intent-filter>
        </service>

Now in App2, I will try to access the service to get the result. You need to bind via explicit intent service with the client by ServiceConnection.

public class MultiplyNumberAidlActivity extends AppCompatActivity {

    @BindView(R.id.editText_First)
    EditText firstNumEditText;
    @BindView(R.id.editText_Second)
    EditText secondNumEditText;
    @BindView(R.id.addButton)
    Button btnAdd;

    MultiplyNumberAidl aidlService;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        ButterKnife.bind(this);

        initView();
    }

    private void initView() {

        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String firstNumber = firstNumEditText.getText().toString().trim();
                String secondNumber = secondNumEditText.getText().toString().trim();
                int multiply= -1;
                if (firstNumber.isEmpty() && secondNumber.isEmpty()){
                    Toast.makeText(MultiplyNumberAidlActivity.this, "Please enter first and second number.", Toast.LENGTH_SHORT).show();
                }else{
                    try {
                        multiply =  aidlService.multiply(Integer.valueOf(firstNumber), Integer.valueOf(secondNumber));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    Toast.makeText(MultiplyNumberAidlActivity.this, ""+multiply, Toast.LENGTH_SHORT).show();
                }
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent("com.example.testapp.bindservice.ipc.aidl.MultiplyNumberAidl");
        Intent updateIntent = createExplicitFromImplicitIntent(MultiplyNumberAidlActivity.this, intent);
        bindService(updateIntent, serviceConnection, Context.BIND_AUTO_CREATE);

    }

    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        //Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        //Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        //Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        //Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);

        //Set the component to be explicit
        explicitIntent.setComponent(component);

        return explicitIntent;
    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlService =  MultiplyNumberAidl.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
             aidlService = null;
        }
    };
}

Here is the xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <EditText
        android:maxLength="3"
        android:inputType="number"
        android:id="@+id/editText_First"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter First Number"/>

    <EditText
        android:maxLength="3"
        android:inputType="number"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/editText_First"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText_Second"
        android:hint="Enter Second Number"/>

   <Button
        android:layout_marginTop="10dp"
        android:layout_below="@+id/editText_Second"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Multiply"
        android:id="@+id/addButton"
        android:background="@color/colorPrimary"
        android:textColor="#fff"/>

</RelativeLayout>

Wow AIDL is the really awesome feature of service that running in the background in multi-thread environment and its IPC mechanism is very strong to communicate remotely.

I  used the reference from my old post which is posted here and here. Please do subscribe your email to get every newsletter from this blog and if you feel that this post helps you then do not forget to share and comment below.

Happy Coding 🙂

0 0 votes
Article Rating

Recent Posts

Hide your production API key or any sensitive data in Android

Hi everyone, In this article, we are going to learn how to hide the production… Read More

2 years ago

How to handle the localisation or multi language support in android with examples?

Hello everyone, Today in this article, we are going to learn about localisation to support… Read More

2 years ago

How to convert any callback to Coroutines and use them in Kotlin Android?

Hello everyone, In this article, we are going to learn something to handle the callback… Read More

2 years ago

Request Permission Launcher with Kotlin in Android

In this article, we are learning about the run time permissions for request permission launchers.… Read More

2 years ago

Implement the SMS User Consent API and SMS Retriever API in Android

Hello everyone. In my last tutorial, we learned about the Jetpack Compose introduction and about applying the… Read More

3 years ago

Jetpack Compose Coroutine flow with LiveData/ViewModel in Android

Hello everyone, In this article, we are going to learn about the Jetpack Compose with… Read More

3 years ago