Introduction
As in the previous article we have learned about binding the service to an activity using extending the Binder class. As we had listed three methods in the previous article that activity could communicate with. like using Binder class and using messenger and AIDL. Before that, let's have a look at previous articles of the series,
Messenger is used to creating inter-process communication (IPC). There are certain steps to create that type of communication in which communication is bidirectional; that is, between two processes.
This is also a client-server architecture let's say, a request is made from MainActivity to access the method in LocalService class.
A response generated in the LocalService class can be forwarded to MainActivity.
Steps to use Messenger
- The service class will implement a Handler that receives a callback for each call from a client(MainActivity).
- In this case, using Handler to create a Messenger object (which is a reference to the Handler).
- The Messenger creates an IBinder that the service returns to clients from onBind().
- Client Activity use the IBinder to instantiate the Messenger (that references the service's Handler), which the client uses to send Message objects to the service.
The service will receive all the messages from client activity in the handleMessage() method of IncomingHandler class we have created that extends Handler class. Again as seen previously, service connection establishment is necessary and initializes the Messenger class object.
activity_main.xml
Creating button and text view to initiate communication and setting data respectively.
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.example.gkumar.servicebindermessanger.MainActivity">
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Get Data"
- android:id="@+id/button"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true" />
- <TextView
- android:id="@+id/textView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Fetching Data From Api..."
- android:layout_below="@+id/button"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="82dp"
- android:visibility="invisible"/>
- </RelativeLayout>
Now find these views created above in MainActivity.java as given below.
- package com.example.gkumar.servicebindermessanger;
-
- import android.content.BroadcastReceiver;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.ServiceConnection;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.Messenger;
- import android.os.RemoteException;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.TextView;
- import android.widget.Toast;
-
- public class MainActivity extends AppCompatActivity {
-
- Messenger mService = null;
- boolean mBound = false;
- TextView fetchDataTextView;
- Button mButton;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mButton = (Button) findViewById(R.id.button);
- fetchDataTextView = (TextView) findViewById(R.id.textView);
-
- mButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- fetchDataTextView.setVisibility(View.VISIBLE);
- onButtonClick(v);
- }
- });
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- Intent intent = new Intent(this, LocalService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
-
- if (mBound) {
- unbindService(mConnection);
- mBound = false;
- }
- }
-
- public void onButtonClick(View v) {
- if (mBound) {
-
- Message msg = Message.obtain(null, LocalService.FETCH_DATA_FROM_API, 0, 0);
- msg.replyTo = replyMessenger;
- try {
- mService.send(msg);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
-
-
-
- private ServiceConnection mConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder service) {
-
- Toast.makeText(MainActivity.this, "connected", Toast.LENGTH_SHORT).show();
- mService = new Messenger(service);
- mBound = true;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- mService = null;
- mBound = false;
- }
- };
-
- Messenger replyMessenger = new Messenger(new HandlerReplyMsg());
-
-
-
- class HandlerReplyMsg extends Handler {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- String recdMessage = msg.obj.toString();
- Toast.makeText(MainActivity.this, "Response Fetched", Toast.LENGTH_LONG).show();
- fetchDataTextView.setText(String.valueOf(recdMessage));
- }
- }
- }
Here, we can see the scenario is completely different to send and receive data between service and activity. Both sides are using handlers to handle communications bidirectional information interchange. See the code written; in onButtonClick() method we are just sending the request to the Local Service class using the messenger.
Now you can see a handler for handling of the replied messages or response from service class. In this handleMessage() just getting message and String type data received and set in the text view simply.
Let's have a look at our service part, say LocalService.java :
- package com.example.gkumar.servicebindermessanger;
-
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.Messenger;
- import android.os.RemoteException;
- import android.util.Log;
- import android.widget.Toast;
-
- import com.android.volley.Request;
- import com.android.volley.Response;
- import com.android.volley.VolleyError;
- import com.android.volley.toolbox.StringRequest;
-
- import java.util.Random;
-
-
-
-
- public class LocalService extends Service {
- Messenger replyMessanger;
- static final int FETCH_DATA_FROM_API = 1;
- public static String responseData;
-
- @Override
- public IBinder onBind(Intent intent) {
- Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_LONG).show();
- return mMessenger.getBinder();
- }
-
-
-
-
- class IncomingHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case FETCH_DATA_FROM_API:
-
- Toast.makeText(LocalService.this,"Request Recieved",Toast.LENGTH_SHORT).show();
- replyMessanger = msg.replyTo;
- getDataFromAPI();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
- final Messenger mMessenger = new Messenger(new IncomingHandler());
-
-
-
- public void getDataFromAPI() {
-
- String url = "http://api.androidhive.info/contacts/";
- StringRequest sr = null;
- sr = new StringRequest(Request.Method.GET, url,
- new Response.Listener<String>() {
- @Override
- public void onResponse(String response) {
- if (response != null) {
- responseData = response.toString();
- if (replyMessanger != null)
- try {
- Message message = new Message();
- message.obj = responseData;
- replyMessanger.send(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
- }, new Response.ErrorListener() {
-
- @Override
- public void onErrorResponse(VolleyError error) {
- Log.e("volleyerror", error.toString());
-
- }
- });
- VolleySingleton.getInstance(this).getRequestQueue().add(sr);
- }
-
- }
Let's analyze this code before starting and request received in handleMessages() of IncomingHandler we can see that in the onBind() method returns the messenger class object to MainActivity then only it gets bound. Now request is received in handleMessage(), a method called there is getDataFromApi() which returns a String response and data send back to MainActivity via Messenger object.
Don't forget to add following code to Manifest file.
Add permission to the Internet and register the service as well.
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.gkumar.servicebindermessanger">
- <uses-permission android:name="android.permission.INTERNET" />
- <application
- android:allowBackup="true"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@style/AppTheme">
- <activity android:name=".MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <service
- android:name="com.example.gkumar.servicebindermessanger.LocalService"
- android:enabled="true"
- android:exported="false"
- />
-
- </application>
-
- </manifest>
Implementation of Android's volley Library
Put this code as it is will help to create volley request and fetching response from API.
VolleySingleton.java
- package com.example.gkumar.servicebindermessanger;
-
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.support.v4.util.LruCache;
-
- import com.android.volley.RequestQueue;
- import com.android.volley.toolbox.ImageLoader;
- import com.android.volley.toolbox.Volley;
-
- public class VolleySingleton {
- private static VolleySingleton mInstance = null;
- private RequestQueue mRequestQueue;
- private ImageLoader mImageLoader;
-
- private VolleySingleton(Context context){
- mRequestQueue = Volley.newRequestQueue(context);
- mImageLoader = new ImageLoader(this.mRequestQueue, new ImageLoader.ImageCache() {
- private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
- public void putBitmap(String url, Bitmap bitmap) {
- mCache.put(url, bitmap);
- }
- public Bitmap getBitmap(String url) {
- return mCache.get(url);
- }
- });
- }
-
- public static VolleySingleton getInstance(Context context){
- if(mInstance == null){
- mInstance = new VolleySingleton(context);
- }
- return mInstance;
- }
-
- public RequestQueue getRequestQueue(){
- return this.mRequestQueue;
- }
-
- }
build.gradle
Now add Gradle dependencies to build.gradle (app) and sync project.
- apply plugin: 'com.android.application'
-
- android {
- compileSdkVersion 23
- buildToolsVersion "23.0.2"
-
- defaultConfig {
- applicationId "com.example.gkumar.servicebindermessanger"
- minSdkVersion 15
- targetSdkVersion 23
- versionCode 1
- versionName "1.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- }
-
- dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- testCompile 'junit:junit:4.12'
- compile 'com.android.support:appcompat-v7:23.1.1'
- compile 'com.android.volley:volley:1.0.0'
- }
Running the Application
- Initiating the service using clicking the button and see the toast shows binding because onBind() method is called
- Request Received in handleMessage() of handler in LocalService.java Class you can see the toast showing "request received".
- Response Fetched in MainActivity, data received in handleMessage() of MainActivity.java as shown in code above and set it into textview.
Summary
Now, considering the previous articles, this article is little bit different, this is IPC via messenger. This article shows the use of handlers to enhance communication between service and activity.
Read more articles on Android