Tuesday 1 July 2014

Android Bound Service using Messenger

Android Service Creation using Messenger 

Feature: Get Reply from Service component to UI using Broadcast Receiver.

Different Android Services in Android


For full description of Android Service go to this post

Messenger Service in Android

  • For Asynchronous Communication
  • Service and Client may live in different Address Space
  • Single thread for all requests
  • No Multi Threading

What Service Needs

  • Create a Handler and Override its handleMessage()
  • Create a Messenger and pass the Handler to it
  • Override onBind()
    • It returns the IBinder using Messenger
  • Receive Message : By Handler
  • Send Message     : msg.replyTo.send(response)


public class MessengerService extends Service {
     
    class IncomingHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           ......
           ......
                  try {

                      msg.replyTo.send(res);

                  } catch (RemoteException e) {                     
                      e.printStackTrace();
                  }
                                   
           }       
    }
   
    final Messenger mMessenger = new Messenger(new IncomingHandler());
  
    @Override
    public IBinder onBind(Intent intent) {
       .......
       return mMessenger.getBinder();
    }
}

What Client Needs

  • Messenger Reference
  • ServiceConnection
    • Override onServiceConnected()
      • Create a new Messenger using IBinder returned from Service
    • Override onServiceDisconnected
  • Call to bindService()
  • Send the Message to Service : Using messenger Reference
  • Receive the Message from the Service: Handler which is set by
    • msg.replyTo.send(new Messenger(new MyResponseHandler))



public class MainActivity extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;
      
    private ServiceConnection mConnection = new ServiceConnection();       

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

    @Override
    protected void onStart() {
       super.onStart();
     
          bindService(new Intent(getApplicationContext(), MessengerService.class),  
                                                 mConnection, Context.BIND_AUTO_CREATE);    
     }
     
    public void sendRequest(View v) {
              Message msg = Message.obtain(null, MessengerService.REQUEST, 0, 0);
              msg.replyTo= new Messenger(new ResponseHandler());
                  mService.send(msg);
           }
          
             
    class ResponseHandler extends Handler {

      @Override
      public void handleMessage(Message msg){
          int mKind=msg.what;          
          switch(mKind){
              case MessengerService.RESPONSE:
                  // Use the Response Message
              default:
          }
                 
      }
    }   
      
    @Override
    protected void onStop() {
       super.onStop();      
       unbindService(mConnection);
       }
}



What Service Connection Does for the Clients

  • Confirm connection with the Service
  • It gets the IBinder returned from the Service
  • Creates Messenger using this for the client
  • Client uses Messenger to get Handler
  • Client sends Message using this Handler


private Messenger mService;
boolean mBound=false;
......

private ServiceConnection mConnection = new ServiceConnection() {

       public void onServiceConnected(ComponentName className, IBinder service) {          
           mService = new Messenger(service);
           mBound = true;          
           Toast.makeText(getApplicationContext(), "Server is connected now !", Toast.LENGTH_SHORT).show();
         
       }

       public void onServiceDisconnected(ComponentName className) {         
           // unexpectedly disconnected -- that is, its process crashed.
           mService = null;
           mBound = false;          
       }
    }

What a Response Handler Does?
  • It gets the Intent sent from Service
  • Send the reply message to UI, the Client

class ResponseHandler extends Handler {

      @Override
      public void handleMessage(Message msg){
          int mKind=msg.what;          
          switch(mKind){
              case MessengerService.RESPONSE:
                  
                  String res=msg.getData().getString("REPLY"); 
                     
                  responseET.setText(res); // UI sets its View using the reply string.
                  
              default:
          }
                 
      }
    }

A live Example
The example application just send a request string from the user to the service and service reply the message string along with a random number to the UI client which it display on its View. The Start Service button is used to start the service. Once service started you can send the request message. To stop the service use Stop Service button.





activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="pp.messengerservice.MainActivity"
    tools:ignore="MergeRootFrame" >

    <TextView
       android:id="@+id/textView1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_alignParentTop="true"
       android:text="Messenger Service Test"
       android:textSize="25sp"
       android:textColor="#ff0000" />

    <Button
       android:id="@+id/button1"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_below="@+id/textView1"
       android:layout_marginTop="28dp"
       android:onClick="sayHello"
       android:text="Start Service" />

    <Button
       android:id="@+id/button2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_below="@+id/button1"
       android:layout_marginTop="20dp"
       android:text="Stop Service" />

    <TextView
       android:id="@+id/textView2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_below="@+id/button2"
       android:layout_marginTop="75dp"
       android:text="Request"
       android:textSize="25sp"
       android:layout_marginRight="5dp"/>

    <EditText
       android:id="@+id/editText1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignBaseline="@+id/textView2"
       android:layout_alignBottom="@+id/textView2"
       android:layout_toRightOf="@+id/textView3"
       android:ems="10"
       android:hint="Put Message !" />

    <EditText
       android:id="@+id/editText2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_below="@+id/editText1"
       android:layout_marginTop="25dp"
       android:layout_toRightOf="@+id/textView3"
       android:ems="10"
       android:hint="Response from Server"
       android:background="#f0fcf2"
       android:textColor="#ff0000" />

    <TextView
       android:id="@+id/textView3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignBaseline="@+id/editText2"
       android:layout_alignBottom="@+id/editText2"
       android:layout_alignParentLeft="true"
       android:text="Response"
       android:textSize="25sp"
       android:layout_marginRight="5dp" />

    <Button
       android:id="@+id/button3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentBottom="true"
       android:layout_marginBottom="24dp"
       android:layout_toRightOf="@+id/textView2"
       android:text="Get Message"
       android:onClick="sendRequest" />

</RelativeLayout>

Manifest.xml
Register the service in manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pp.messengerservice"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
       android:minSdkVersion="15"
       android:targetSdkVersion="19" />

    <application
       android:allowBackup="true"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
     
       <service android:name="pp.messengerservice.MessengerService"/>
     
       <activity
           android:name="pp.messengerservice.MainActivity"
           android:label="@string/app_name" >
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />

               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
    </application>

</manifest>
MessengerService.java

package pp.messengerservice;

import java.util.Random;

import android.app.Service;
import android.content.Intent;
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.widget.Toast;

public class MessengerService extends Service {
  
    static final int REQUEST = 1;
    static final int RESPONSE= 2;
   
    Random gen =new Random();
  
    class IncomingHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           switch (msg.what) {
               case REQUEST:
                 
                   String rq = msg.getData().getString("REQUEST");                  
                   Message res=Message.obtain(null,RESPONSE);
                   Bundle bundle = new Bundle();
                   int random = gen.nextInt(1000);
                   bundle.putString("REPLY", rq+" : "+random);
                   res.setData(bundle);
                  try {
                      msg.replyTo.send(res);
                  } catch (RemoteException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
                   break;
               default:
                   super.handleMessage(msg);
           }
       }
    }
   
    final Messenger mMessenger = new Messenger(new IncomingHandler());
  
    @Override
    public IBinder onBind(Intent intent) {
       Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
       return mMessenger.getBinder();
    }
}
MainActivity.java

package pp.messengerservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;
    boolean mBound;
   
    Button startService, stopService;
    EditText requestET, responseET;
   
    public void sendRequest(View v) {
       if (!mBound) {
           Toast.makeText(getApplicationContext(), "Please Satrt The Service !", Toast.LENGTH_SHORT).show();
          return;  
       }
       Message msg = Message.obtain(null, MessengerService.REQUEST, 0, 0);
       Bundle bundle = new Bundle();
       String req = requestET.getText().toString();
       bundle.putString("REQUEST", req);
       msg.setData(bundle);
       msg.replyTo= new Messenger(new ResponseHandler());
       try {
           mService.send(msg);
       } catch (RemoteException e) {
           e.printStackTrace();
       }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
       public void onServiceConnected(ComponentName className, IBinder service) {          
           mService = new Messenger(service);
           mBound = true;          
           Toast.makeText(getApplicationContext(), "Server is connected now !", Toast.LENGTH_SHORT).show();
         
       }

       public void onServiceDisconnected(ComponentName className) {         
           // unexpectedly disconnected -- that is, its process crashed.
           mService = null;
           mBound = false;          
       }
    };
   
    class ResponseHandler extends Handler {

      @Override
      public void handleMessage(Message msg){
          int mKind=msg.what;          
          switch(mKind){
              case MessengerService.RESPONSE:
                  responseET.setEnabled(true);
                  String res=msg.getData().getString("REPLY");                      
                  responseET.setText(res);
                  responseET.setEnabled(false);
              default:
          }
                 
      }
    }

   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
     
       startService = (Button)findViewById(R.id.button1);
       stopService = (Button)findViewById(R.id.button2);
       requestET=(EditText)findViewById(R.id.editText1);
       responseET=(EditText)findViewById(R.id.editText2);
     
       stopService.setEnabled(false);
       requestET.setEnabled(false);
       responseET.setEnabled(false);

    }

    @Override
    protected void onStart() {
       super.onStart();
     
       startService.setOnClickListener(new View.OnClickListener() {          
          @Override
          public void onClick(View arg0) {
              // TODO Auto-generated method stub
              bindService(new Intent(getApplicationContext(), MessengerService.class), mConnection,
                      Context.BIND_AUTO_CREATE);    
              startService.setEnabled(false);
               stopService.setEnabled(true);              
               requestET.setEnabled(true);
          }
      });
     
       stopService.setOnClickListener(new View.OnClickListener() {
         
          @Override
          public void onClick(View v) {
              // TODO Auto-generated method stub
               if (mBound) {
                      unbindService(mConnection);
                      mBound = false;
                      startService.setEnabled(true);
                      stopService.setEnabled(false);                     
                      requestET.setEnabled(false);                     
                      Toast.makeText(getApplicationContext(), "Server is Disconnected !", Toast.LENGTH_SHORT).show();
                  }
          }
      });      
    }

    @Override
    protected void onStop() {
       super.onStop();      
       if (mBound) {
           unbindService(mConnection);
           mBound = false;
       }
    }
}

No comments:

Post a Comment