Monday 16 June 2014

Android Bound Service Concept


Bound Service


Android bound service is a service component of an android application. Other component like, activity has to bind to this kind of service to initiate the communication between them. This is achieved via a method call of bindService() from activity class or whatever the other component is. Bound service will not run forever like “Started Service” and will be destroyed by the system once the caller is bind to it or you can say the caller has not called unbindService method. In my earlier post which has detail explanation about the android service. If you want to get a look over the service component please visit that page.



A bound service allows components (such as activities) to bind to the service, send requests, receive responses, and even perform inter-process communication (IPC). A bound service typically lives only while it serves another application component and does not run in the background indefinitely


This is kind of inter process communication (IPC) between android components like activity, broadcastreceiver or content provider and the bound service. In these kind of scenario bound services act as a SERVER and other component are clients. remember services are not restart on rotation of the device, it will stay to run the same instance unlike the activity. Bound services provides an interface for its client to access the public methods of the service. These public methods provide the actual service for its client. Client component like UI thread bind to this service using an Intent and ServiceConnection object to access it.


Lifetime of Bound Service


Bound Service live till at least one client component is bind to it. Suppose multiple clients are bind to it and this service is providing services to all these clients and some clients call unbindService but not all, then the service will have a life and it will still be running. As such, you don't have to manage the lifecycle of your service if it's purely a bound service—the Android system manages it for you based on whether it is bound to any clients.


Types of Bound Service


  • Local Bound Service
  • Remote Bound Service


A bound service can be a local or remote one. Before creating a bound service, you have to think some of the key points to implement this. Some of them are whether this bound service will be used within the same application or not. If it is like this then create a local bound service using IBinder class. Local bound service will run in the same process space in which service clients are running. So all the components are aware of this bound service.


A local bound service can be created using Binder class and for creating remote bound service you need either Messenger class or AIDL mechanism.


Second most important thing to consider is whether you want processing of clients requests in parallel or sequentially. To process requests parallely you have to create threads in such manner.  




Create Local Bound Service Using Binder


First let us create a skeleton to create a local bound service using Binder.


public class LocalBoundService extends Service {
  
// IBinder is the Interface between Client and Server which returns this service
  private final IBinder mBinder = new LocalBinder();

  private final Random gen = new Random();

  public class LocalBinder extends Binder {
      LocalBoundService getService() {
          // Return this instance of LocalService so clients can call public methods
          return LocalBoundService.this;
      }
  }

  @Override
  public IBinder onBind(Intent intent) {
      return mBinder;
  }

 // Public methods to be called by clients

  public int getRandomNumber() {
     return gen.nextInt(1000);
  }
}


In the above code snippet, a custom class LocalBoundService is created which extends the Service class. You need to override the onBind method of the service class which returns an IBinder instance on successful connection with the client. Client uses this binder to communicate the service. The binder instance that returns by the onBind method is assigned to a LocalBinder object which is a Binder type. Inside the LocalBinder, one method is defined, getService. Once client get the binder instance inside the ServiceConnection object, it returns the service itself to the client. The client now can call the public methods of the local service class. Below code snippet of the client which is an activity, uses this service to get  random number using the public method of the local service.


Create a Client for Local Bound Service


public class BindingActivity extends Activity {

// Define a Local Bound Service Reference
  LocalBoundService mService;
  boolean mBound = false;
  
  @Override
  protected void onStart() {
      super.onStart();
      Intent intent = new Intent(this, LocalService.class);
// Bind with the bound service with this activity component
      bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  }

// Use the Local Service Reference to call public methods of Service
   public void onClick(View v){     
        int num = mService.getRandomNumber();    
   }          

//  Defines callbacks for service binding, passed to bindService()
  private ServiceConnection mConnection = new ServiceConnection() {
    @Override
   public void onServiceConnected(ComponentName className,
             IBinder service)
            {
                LocalBinder binder = (LocalBinder) service;
                mService = binder.getService();
                mBound = true;
       }
  };
}


Interaction Between Server and Client
Below diagram shows the interaction between client (Activity) and service component. Client needs a ServiceConnection Object to call bindService method. This bindService is asynchronous call, so you can get the immediate information about the successful connection with the service. This service connection information is received inside the onServiceConnected method of ServiceConnection object. On successful connection, you can call the public methods of the service. Once client is done with the service, it call unbindService method and system will destroy the service.


Create Remote Bound Service
  • Using Messenger
  • Using AIDL


A service can be accessed by another application also. To create such remote service android provide two mechanism. You can use Messenger class or use AIDL to implement these kind of services.we will discussed in next posts.




Sunday 15 June 2014

Android Intent Service



Intent Service Overview


Android provides two types of services started and bound. A started service is started by the system when its client (ex- Activity) call startService method from its own content. The started service once started, will run forever until it is self stopped by calling stopSelf method or client stop it by calling stopService method. A bound service is created by the system when a client call bindService instead of the startService method. The bound service will run only till the client is bound to it. It will not run forever like started service.


Started service can be implemented by either extending IntentService class or by extending Service class. IntentService class provide a very simple way to create a service. The started service is locally available only, meaning that the client must be in the same process in which service is. This is not accessible by the remote client.


These services are best suited for a single request from the client. For example service like sending a file from android client to a web server. The client application activity component will call the IntentService service to perform the network operation to send the file. Here sending a file a service which activity use it for sending.


In this post, we will learn how to create a IntentService based started service through a live example. The custom service class need to extends the IntentService class. You have to override the onHandleIntent method of the class. If the service is performing a time consuming task or some network operation then, you have to create a separate thread over which these operation will be performed. It will degrade the user experience for time consuming tasks.


Implement Started Service by Extending IntentService class


Public class MyStartedService extends IntentService {
..
..
@Override
protected void onHandleIntent (Intent intent) {
..
..
}
}





Define your service in the manifest.xml
It is required to define your service in the manifest so that other component can use it. It is done statically here. You can register dynamically through program also.


<application android:icon="@drawable/icon" android:label="@string/app_name"
       android:theme="@android:style/Theme.Holo.Light">
       <service android:name=".MyDownloadService"/>
       <activity android:name=".IntentServiceActivity"
           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>


Some insight into Intentservice
A service is started by another component of Android (like Activity) by invoking startService() method. Intent Service Creates a Handler thread thread in the background which processes Intent command. It creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading. The system stops the service after all start requests have been handled, so you never have to call stopSelf(). it provides default implementation of onBind() that returns null. It provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.


Below diagram shows the flows of interaction between the IntentService and its client (Activity). I hope this will help you in understanding the service in a better way. The service depicted below is a download service which is used by the activity.



An Example


The sample example contains a button which starts the service on clicking and the service display a webpage whose URL is entered in the edit text box. Service also return a string which is used to update the text vies of main UI thread.




MainActivity.java


package pp.myintentservice;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


public class MainActivity extends Activity {

           Button serviceButton;
           TextView tv;  
           WebView wv;
          
           Intent serviceIntent;
           MyDownloadServiceReceiver breciever;          
          
           @Override
           protected void onCreate(Bundle savedInstanceState) {
                       super.onCreate(savedInstanceState);
                       setContentView(R.layout.activity_main);
                      
                       tv=(TextView)findViewById(R.id.textView2);                     
                       serviceButton = (Button)findViewById(R.id.button1);
                       wv=(WebView)findViewById(R.id.webView1);

                       IntentFilter intentFilter = new IntentFilter("DownloadService");
                       intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
                       breciever = new MyDownloadServiceReceiver();
                       registerReceiver(breciever, intentFilter);
                      
                       serviceIntent=newIntent(getApplicationContext(), MyDownloadIntentService.class);                                                                   
                       serviceButton.setOnClickListener(new View.OnClickListener() {                               
                                                                      
                                   @Override
                                   public void onClick(View arg0) {        
                                              
                                               EditText selection=(EditText)findViewById(R.id.editText1);
                                               String url=selection.getText().toString();                                           
                                               System.out.println("Entered URL is..."+url);
                                               serviceIntent.putExtra("REQUEST", url);
                                               startService(serviceIntent);
                                   }
                       });
                      
           }



           class MyDownloadServiceReceiver extends BroadcastReceiver{             

                       @Override
                       public void onReceive(Context context, Intent intent) {
                                   // TODO Auto-generated method stub
                                   String rep=intent.getStringExtra("RESPONSE");                            
                                   tv.setText(rep);
                                  
                                   String web=intent.getStringExtra("WEB");
                       //          wv.getSettings().setJavaScriptEnabled(true);
                                   wv.getSettings().getMinimumFontSize();
                                   try {
               wv.loadData(URLEncoder.encode(web,"utf-8").replaceAll("\\+"," "),
                                   "text/html", "UTF-8");
           } catch (UnsupportedEncodingException e) {
               e.printStackTrace();
           }
                       }
           }
}



MyDownloadIntentService.java


package pp.myintentservice;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

public class MyDownloadIntentService extends IntentService {
          
           public MyDownloadIntentService() {
                       super("PP");
                       // TODO Auto-generated constructor stub
           }

           @Override
           protected void onHandleIntent(Intent intent) {
                       // TODO Auto-generated method stub                       
                       String url=intent.getStringExtra("REQUEST");                  
                       System.out.println("Requested URL is: "+url);                     
                       String reply= "Hi From Service !!!";
                                                                                              
                       try{
                       HttpClient httpclient = new DefaultHttpClient();
                       String responseMessage="";                            
                       HttpGet httpGet = new HttpGet(url);
       HttpResponse response = httpclient.execute(httpGet);
       StatusLine statusLine = response.getStatusLine();
      
       if(statusLine.getStatusCode() == HttpStatus.SC_OK){
           ByteArrayOutputStream out = new ByteArrayOutputStream();
           response.getEntity().writeTo(out);
           out.close();
           responseMessage = out.toString();
          
           Intent bIntent = new Intent ("DownloadService");
                                   bIntent.addCategory(Intent.CATEGORY_DEFAULT);
                                   bIntent.putExtra("RESPONSE", reply);                              
                                   bIntent.putExtra("WEB", responseMessage);                                                           
                                   sendBroadcast(bIntent);
       }

       else{
           //Closes the connection.
           Log.w("HTTP1:",statusLine.getReasonPhrase());
           response.getEntity().getContent().close();
           throw new IOException(statusLine.getReasonPhrase());
       }

   } catch (Exception e) {
     
   }                                                                             
           }         
}


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.myintentservice.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:layout_marginTop="10dp"
       android:text="Intent Service Test"
       android:textAppearance="?android:attr/textAppearanceLarge" />

   <FrameLayout
       android:id="@+id/iml"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_below="@+id/textView2"
       android:layout_centerHorizontal="true"
       android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
         android:layout_alignParentBottom="true"
       android:layout_marginTop="18dp" >

       <WebView
           android:id="@+id/webView1"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

   </FrameLayout>

   <EditText
       android:id="@+id/editText1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_below="@+id/textView1"
       android:ems="10"
       android:hint="Enter URL"
       android:inputType="textWebEditText" />

   <Button
       android:id="@+id/button1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_below="@+id/editText1"
       android:text="Start Service" />

   <TextView
       android:id="@+id/textView2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_below="@+id/button1"
       android:text="Wait for service Reply"
       android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>


AndroidManifest.xml


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

   <uses-sdk
       android:minSdkVersion="15"
       android:targetSdkVersion="19" />
   <uses-permission android:name="android.permission.INTERNET"/>

   <application
       android:allowBackup="true"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
       <service android:name="pp.myintentservice.MyDownloadIntentService"/>
       <activity
           android:name="pp.myintentservice.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>