KIO
Kreative Ideen online
Bound service

Bound service

Started services are great for background operations, but what if you need a service that’s more interactive?

… a type of service your activity can interact with.

Bound services are bound to other components…

A bound service is one thats’s bound to another application component, such as an activity. Unlike a started service, the component can interact with the bound service and acall its methods.

public class OdometerService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
    }
}

The above code implements one method, onBind(), which gets called when a component, such as an activity, wants to bind tho the service. It has one parameter, an Intent, and returns an IBinder object. IBinder is an interface that’s used to bind your servie to the activity, and you need to provied an implementation of it in your service code.

Implement a binder

You implement the IBinder by adding a new inner class to your service code that extends the Binder class(which implements the IBinder interface). This inner class needs to include a method that activities can user to get a reference to the bound service.

//When you create a bound service, you need to provide a Binder implementation
public class OdometerBinder extends Binder{
   //The activity will use this method to get a reference to the OdometerService
   OdometerService getOdometer(){
       return OdometerService.this;
   }
}

We need to return an instance of the OdometerBinder in OdormeterService’s onBind() method. To do this, we’ll create a new private vaiable for the inder, instantiate it, and return it in the onBind() method.

package eu.kio.odometer;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Binder;

import java.util.Random;

public class OdometerService extends Service {
   //We're using a private final variable for our IBinder object
   private final IBinder binder = new OdometerBinder();

   //This is our IBinder implementation
   piblic class OdometerBinder extends Binder{
        OdometerService getOdometer(){
            return OdometerService.this;
        }
   }    

   @Override
   public IBinder onBind(Intent intent){
      //Return the IBinder
      return binder;
   }
}

Add a getDistance() method to the service

We’re going to add a method to OdometerService called getDistnce(), which our activity will call. We’ll get it to return a random number.

Here’s the full code…

package eu.kio.odometer;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Binder;

import java.util.Random;

public class OdometerService extends Service {
    //We're using a private final vaiable for our IBinder-object
    private final IBinder binder = new OdometerBinder();
    //We'll use Random() object to generate random numbers
    private final Random random = new Random();

    //This is our IBinder implementation
    public class OdometerBinder extends Binder{
        OdometerService getOdometer(){
            return OdometerService.this;
        }
    }

    public OdometerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //Return the IBinder
        return binder;
    }

    //Add the getDistance method
    public double getDistance(){
        return random.nextDouble();
    }
}

Get an activity to bind to OdometerService and call its getDistance() method.

a TextView will display the number returned by OdometerService’s getDistance() method

What the activity needs to do

To get an activity to connect to a bound service and call its methods, there are a few steps you need to perform:

  • Create a service Connection
    This uses the service’s IBiner object to form a connection with the service
  • Bind the activity to the service
    Once you’ve bound it to the service, you can call the service’s methods directly
  • Interact with the service
    In our case, we’ll use the service’s getDistance() method to update the acteivity’s text view
  • Unbind from the service when you’ve finished with it
    When the service is no longer used,Android destroys the service to free up resources

Create a ServiceConnection

A ServiceConnection is an interface that enables your activity to bin to a service.It hast two methods that you need to define:

  • onServiceConnected()
  • onServiceDisconnected()

The onServiceConnected() method is called when a connection to the sercie is established.

The onServiceDisconected() is called when it disconnects.

Here’s what the basic code looks like:

package eu.kio.odometer;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends Activity {

    private OdometerService odometer;
    private boolean bound = false;

    //Create a Service Connection object
    private ServiceConnection connection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName componentName,IBinder binder){
            //Code that runs when the service is connected
        }
        @Override
        public void onServiceDisconnected(ComponetName componentName){
            //Code that runs when the service is disconnected
        }
    };

The onServiceConnceted() method is called when a connection is established between the activity and the service. It takes two parameters:

  • ComponentName
    this object describes the service that’s been connected
  • IBinder
    this objet that’s defined by the service

There are two things we need the onServiceConnected() method to do:

  • Use it’s IBinder parameter to get a reference to the service we’re connected to
    We can do this by casting the IBinder to an OdometerService.OdometerBinder (as this is the type of IBinder we defined in OdometerService) and calling its getOdometer() method.
  • Record that the activity is bound to the service

Here’s the code to do these things:

.....

public class MainActivity extends Activity {

    private OdometerService odometer;
    private boolean bound = false;

    //Create a Service Connection object
    private ServiceConnection connection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName componentName,IBinder binder){
            //Casting IBinder binder to OdometerService.OdometerBinder
            OdometerService.OdometerBinder odometerBinder = (OdometerService.OdometerBinder) binder;
            //Use the IBinder to get a reference to the service
            odometer = odometerBinder.getOdometer();
            //Record that the activity is bound to the service
            bound = true;
        }
.....

The onServiceDisconnected() method is called when the service and the activity are disconnected. It takes one parameter, a ComponentName object that describes the service.

The only one thing we need the onServiceDisconnected() method to do when it’s called:

  • record that the activity is no longer bound to the service

Here’s the code to do that…

....
        @Override
        public void onServiceDisconnected(ComponentName componentName){
            //Code that runs when the service is disconnected

            //Set bound to false, as MainACtivity is no longer bound to OdometerService
            bound = false;
        }
....

How to bind and unbind from the service

Use bindService() to bind the service

When you bind your service activity to a service, you usually do it in one of two places:

  • In the activity’s onStart() method
    When the activity becomes visible
  • In the activity’s onCreate() method
    When the activity gets crated

We only need to display updates OdometerService when Mainactivity is visible, wo we’ll bind to the service in its onStart() method.

To bind to the service, you first reate an explicit intent that’s directed at the service you want to bind to. You then use the activity’s bindSercice() method to bind to the service, passing it the …

  • the intent
  • the ServicConncetion object defined by the service
  • a flag to descibe how you want to bind

Here’s the code …

....
     @Override
     protected void onStart(){
          super.onStart();
          Intent intent = new Intent(this, OdometerSerivice.class);
          bindService(intent, connection, Context.BIND_AUTO_CREATE);
     }
....

In the above example, we’ve used the flag Context.BIND_AUTO_CRATE to tell Anroid to create the service if it doesn’t already exist. There are other flags you can use instead…
Android documentation:
https://developer.android.com/reference/android/content/Context.html

Use unbindService() to unbind from the service

When you unbind your activity from a service, you usually add the code to your activity’s onStop() or onDestroy() method. It depends on where you put your bindService() code:

  • If you bound to the service in your activity’s onStart() method, unbind from it in the onStop() method
  • If you bound to the service in your activity’s onCreate() method, unbind from it in the onDestroy() method

You unbind from a service using the unbindService() method. The method takes on parameter, the ServiceConnection object.

Here’s the code…

....
   private boolean bound = false;
....
   @Override
   protected void onStop(){
      super.onStop();
      if(bound){
         unbindService(Connection);
         bound = false;
      }
   }
....

In the above code we’re using the value of the bound variable to test whether or not we need to unbind from the service.

The final thing is to get the activity to call OdometerService’s getDistance() method, and display its value

We’re going to call the OdometerServic’s getDistance() method every second, and update the TextView with its value using an Handler

Here’s the method….

 private void displayDistance(){
        //Get the TextView
        final TextView distanceView = (TextView)findViewById(R.id.distance);
        //Create a new Handler
        final Handler handler = new Handler();
        //Call the Handler's post() method, passing in a new Runnable
        handler.post(new Runnable(){
           @Override
           public void run(){
               double distance = 0.0;
               if(bound && odometer != null){
                   //If we've got a reference to the OdometerService and we're bound to it, call getDistance()
                   distance = odometer.getDistance();
               }
               String distanceStr = String.format(Locale.getDefault(),"%1$,.2f miles",distance);
               distanceView.setText(distanceStr);

               /**Post the code in the Runnable to
                * be run again after a delay of 1 second. As this line of code is included in the Runnable
                * run() method.it will run every second (with a slight lag).
                 */
               handler.postDelayed(this,1000);
           }
        });
    }

Full code for context….

OdometerService:

package eu.kio.odometer;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Binder;

import java.util.Random;

public class OdometerService extends Service {
    //We're using a private final vaiable for our IBinder-object
    private final IBinder binder = new OdometerBinder();
    private final Random random = new Random();

    //This is our IBinder implementation
    public class OdometerBinder extends Binder{
        OdometerService getOdometer(){
            return OdometerService.this;
        }
    }

    public OdometerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");

        //Return the IBinder
        return binder;
    }

    public double getDistance(){
        return random.nextDouble();
    }
}

Mainactivity:

package eu.kio.odometer;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends Activity {

    private OdometerService odometer;
    private boolean bound = false;

    //Create a Service Connection object
    private ServiceConnection connection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName componentName,IBinder binder){
            //Code that runs when the service is connected
            OdometerService.OdometerBinder odometerBinder = (OdometerService.OdometerBinder) binder;
            //Use the IBinder to get a reference to the service
            odometer = odometerBinder.getOdometer();
            //The activity is bound to the service, so set the bound variable to true
            bound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName){
            //Code that runs when the service is disconnected

            //Set bound to false, as MainACtivity is no longer bound to OdometerService
            bound = false;
        }
    };

    @Override
    protected void onStart(){
        super.onStart();
        Intent intent = new Intent(this,OdometerService.class);
        bindService(intent,connection,Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop(){
        super.onStop();
        if(bound){
            //This uses the SevicConnection object to unbind from the service
            unbindService(connection);
            //When we unbind, we'll set bound to false
            bound = false;
        }
    }

    private void displayDistance(){
        //Get the TextView
        final TextView distanceView = (TextView)findViewById(R.id.distance);
        //Create a new Handler
        final Handler handler = new Handler();
        //Call the Handler's post() method, passing in a new Runnable
        handler.post(new Runnable(){
           @Override
           public void run(){
               double distance = 0.0;
               if(bound && odometer != null){
                   //If we've got a reference to the OdometerService and we're bound to it, call getDistance()
                   distance = odometer.getDistance();
               }
               String distanceStr = String.format(Locale.getDefault(),"%1$,.2f miles",distance);
               distanceView.setText(distanceStr);

               /**Post the code in the Runnable to
                * be run again after a delay of 1 second. As this line of code is included in the Runnable
                * run() method.it will run every second (with a slight lag).
                 */
               handler.postDelayed(this,1000);
           }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Update the TextView's value every second
        displayDistance();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *