Android Service creation and consumption Tutorial

Posted August 7th, 2008 by

Introduction

This tutorial will show you how to create a simple service, that does not use IPC (inter process communication). Services are great for running long running tasks and business logic, outside an Activity, which is tied to the user interface. For example, if you have a background task that has to download data periodically, then you should put that task in a Service. You can explicitly start a service and stop it as well. With IPC you can connect to a running service and call methods on it, however, in this example, I won’t be using any IPC; instead all data transfer will happen via a shared object and a listener.

Android 101

To get started with Android, click here.

Service creation

There are really 2 steps to creating a service, this is in addition to whatever code you want the service to execute:

  • Create the class – for this you have to extend the Service class. You have to override the onCreate() and onDestroy() methods, which get executed when your service is started and stopped. There are other lifecycle methods that you can override, which are of interest if you’re using IPC, but we are keeping things simple in this tutorial.
  • Update android manifest (AndroidManifest.xml) – You have to list the class name of your Service in this XML file.

The following is an example of a service called MyService that periodically gets some data from a web service, and saves it to a shared object (static object). Here’s the AndroidManifest.xml entry for this service:

    <service android:name=".myservice.MyService"/>


Here’s the Java code for the MyService implementation itself:

package com.developerlife.myservice;

/**
 * MyService
 *
 * @author Nazmul Idris
 * @version 1.0
 * @since Jul 21, 2008, 12:03:01 PM
 */
public class MyService extends Service {

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// constants
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static final String ServletUri = "http://" + AppUtils.EmulatorLocalhost + ":8080/Ping";

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// static data/shared references, etc.
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static ServiceUpdateUIListener UI_UPDATE_LISTENER;
private static MainActivity MAIN_ACTIVITY;

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private static Hashtable<String, Hashtable<DataKeys, String>> weatherData =
    new Hashtable<String, Hashtable<DataKeys, String>>();
private Timer timer = new Timer();
private static final long UPDATE_INTERVAL = 5000;

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// hooks into other activities
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static void setMainActivity(MainActivity activity) {
  MAIN_ACTIVITY = activity;
}

public static void setUpdateListener(ServiceUpdateUIListener l) {
  UI_UPDATE_LISTENER = l;
}

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// lifecycle methods
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

/** not using ipc... dont care about this method */
public IBinder onBind(Intent intent) {
  return null;
}

@Override protected void onCreate() {
  super.onCreate();

  // init the service here
  _startService();

  if (MAIN_ACTIVITY != null) AppUtils.showToastShort(MAIN_ACTIVITY, "MyService started");
}

@Override protected void onDestroy() {
  super.onDestroy();

  _shutdownService();

  if (MAIN_ACTIVITY != null) AppUtils.showToastShort(MAIN_ACTIVITY, "MyService stopped");
}

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// service business logic
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private void _startService() {
  timer.scheduleAtFixedRate(
      new TimerTask() {
        public void run() {
          _getWeatherUpdate();
        }
      },
      0,
      UPDATE_INTERVAL);
  Log.i(getClass().getSimpleName(), "Timer started!!!");
}

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// weather data that the service gets...
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static Hashtable<String, Hashtable<String, String>> DataFromServlet =
    new Hashtable<String, Hashtable<String, String>>();

/** dont forget to fire update to the ui listener */
private void _getWeatherUpdate() {
  // http post to the service
  Log.i(getClass().getSimpleName(), "background task - start");

  Hashtable<String, String> map = new Hashtable<String, String>();
  map.put("key", "value");

  try {

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(map);

    PostMethod post = HttpUtils.sendMonitoredPOSTRequest(ServletUri,
                                                         null,
                                                         new ByteBuffer(baos.toByteArray()),
                                                         null);
    ByteBuffer data = HttpUtils.getMonitoredResponse(null, post);

    ObjectInputStream ois = new ObjectInputStream(data.getInputStream());
    DataFromServlet =
        (Hashtable<String, Hashtable<String, String>>) ois.readObject();

    Log.i(getClass().getSimpleName(), "data size from servlet=" + data.toString());
    Log.i(getClass().getSimpleName(), "data hashtable from servlet=" + DataFromServlet.toString());

  }
  catch (Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    e.printStackTrace(pw);
    Log.e(getClass().getSimpleName(), sw.getBuffer().toString(), e);
  }

  Log.i(getClass().getSimpleName(), "background task - end");

  if (UI_UPDATE_LISTENER != null) {
    UI_UPDATE_LISTENER.updateUI(DataFromServlet);
  }
}

private void _shutdownService() {
  if (timer != null) timer.cancel();
  Log.i(getClass().getSimpleName(), "Timer stopped!!!");
}

}//end class MyService

Notes on the code:

  1. the onBind() method returns null; I’m not using IPC, which is why null is being returned. If you do use IPC then you have to return the stub here.
  2. onCreate() creates the background thread that periodically checks some web service for updates. The web service URI is contained in ServletUri class variable. The background thread is started using a recurring Timer.
  3. onDestroy() shuts down the background thread that periodically checks some web service for updates. This method stops the background timer started in onCreate().
  4. The bulk of the work is performed in _getWeatherUpdate()… this method performs the business logic of the recurring background task. When updates are receieved, notifications are fired to the UI_UPDATE_LISTENER. Also, all data from the network is stored in a static object DataFromServlet – which is used by other components to get this data once the UI_UPDATE_LISTENER is called.

Need more help? developerlife.com offers training courses to empower you, and consulting services to enable you.

Here’s the listener interface that’s invoked when the service gets updates from the network:

public interface ServiceUpdateUIListener {

public void updateUI(Hashtable<String, Hashtable<String, String>> data);

}//end interface ServiceUpdateUIListener


The listener is pretty straighforward. If you want to implement this listener interface in GUI code, so that updates that you get are then drawn to the screen/Activity, then you have to wrap the code in UIThreadUtilities.runInUIThread(), here’s an example:

  MyService.setUpdateListener(new ServiceUpdateUIListener() {
    public void updateUI(final Hashtable<String, Hashtable<String, String>> newData) {
      // make sure this runs in the UI thread... since it's messing with views...
      UIThreadUtilities.runOnUIThread(
          context,
          new Runnable() {
            public void run() {
              if (newData.size() == _data.size()) {
                if (newData.keySet().equals(_data.keySet())) return;
              }

              AppUtils.showToastShort(context,
                                      "Data updated");

              // update the saved weather data
              _data.clear();
              _data.putAll(newData);

              // tell the view to refresh since the model changed.
              WeatherDataListAdapter.this.notifyDataSetChanged();
            }
          });
    }
  });

Notes on this code:

  1. A Runnable implementation is created and passed to runOnUIThread to make sure that the code in it’s run() method is executed in the UI thread. This is important to do, since you don’t want anything outside the UI thread making drawing updates on the UI components.
  2. The data in the newData Hashtable that’s passed to this method is downloaded and created in MyService’s background Timer thread… that’s the extent to what can be done in a background thread…. at this point the data must be visually rendered, which is why it’s done on the UI Thread.

Service consumption

The code so far has shown you how to create a service, and hook it up to UI elements in your project. However, you have to start the service and stop it in your main Activity, so that all sub Activities will have access to the service. To start/stop the service, before you use it, you must:

  • To start : create an intent to start the service (not using IPC bind/unbind) in activity that uses the service
  • To stop : create an intent to stop the service, in activity that uses the service.

Here’s code examples to do this:

public class MainActivity extends SimpleActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
  super.onCreate(icicle);
  try {

    // setup and start MyService
    {
      WeatherService.setMainActivity(this);
      Intent svc = new Intent(this, MyService.class);
      startService(svc, Bundle.EMPTY);
    }

  }
  catch (Exception e) {
    Log.e(Global.TAG, "ui creation problem", e);
  }

}

@Override protected void onDestroy() {
  super.onDestroy();

  // stop MyService
  {
    Intent svc = new Intent(this, MyService.class);
    stopService(svc);
  }

}

Comments are closed.