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
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 🙂
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com