dcsimg
December 3, 2016
Hot Topics:

Executing Long Running Background Tasks in Android Apps Without an Interface (Using Services)

  • January 13, 2016
  • By Vipul Patel
  • Send Email »
  • More Articles »

Introduction To Android Services

Android provides platform support to execute with and without a user interface. An Android service is defined as an application component that is generally used to perform long tasks in the background without needing user input.

Services could be used for a variety of purposes:

  • Handle network transactions
  • Play audio/music in background
  • Perform non-user input requiring I/O operations like backup
  • Periodic downloads of certain types of data

Basics of Android Services

The service isn't a separate process, nor a thread. It runs in the same process as the application. A service provides the following features of interest:

  • Something that the application can communicate to the Android system what it wants to execute in the background without telling user what it is doing
  • A mechanism for an application to expose certain functionality to other applications

A service needs to declare itself in the application manifest file (AndroidManifest.xml) as a <service>.

<manifest ... >
   ...
   <application ... >
      <service android:name=".MyService" />
      ...
   </application>
</manifest>

Services can be started in one of two ways: by using Context.startService() or Context.bindService().

Good programming practices dictate to use an explicit intent when starting or binding a service. One must also not declare intent filters for a service.

How to Create a Service in Android

There are two ways to create a service. If the service is simple, you can create a subclass of IntentService and only implement the onHandleIntent() method.

To create a service in a custom manner, one needs to create a subclass of Service and override some methods (methods that handle key aspects of service lifecycle).

  • onStartCommand(): This method is called when another component requests the service to be started by calling startService().
  • onBind(): This method is called when another component wants to bind to the service by calling bindService().
  • onCreate(): This method is called when the service is first created. This method is meant for "housekeeping." The Android platform calls this method before it calls onStartCommand() or onBind().
  • onDestroy(): This method is called when the service is no longer used and is to be destroyed. All cleanup code should reside in this method.

Note that when Android system runs low on resources, background services can be destroyed to free up resources, so make sure to implement the above method properly.

Hands On with Android Services

Let us now create a simple service—"started service"—that will be started by another component by calling startService().

Fire up Android Studio and start a new Android Studio Project.

Serv01
Figure 1: Starting Android Studio

Provide ServiceDemo as the Application Name and click Next.

Serv02
Figure 2: Naming the application

On the next screen, leave the default values and click Next.

Serv03
Figure 3: Leaving the defaults in place

On the "Add an activity to Mobile" page, choose "Blank Activity". This creates an application with a single activity.

Serv04
Figure 4: Starting a blank activity

We are then prompted to customize the activity. We will leave the default values unchanged.

Serv05
Figure 5: Again, leaving the default values in place

Click Finish to creating the project files.

Next, we create a custom Service. Right-click the /src/java/ folder on the Project Explorer and select New-> Service->Service.

Serv06
Figure 6: Creating a custom Service

Leave the default values unchanged and click Finish to create the Service class.

Serv07
Figure 7: Once again, leaving the default values in place

The default code generated is as follows:

package com.example.vipul.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
   public MyService() {
   }

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

}

We will now implement the other callbacks. We will also add logging to understand the order of the calls.

package com.example.vipul.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
   public MyService() {
   }

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

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      Log.w("MyService", "onStartCommand callback called");
      return super.onStartCommand(intent, flags, startId);
   }

   @Override
   public void onCreate() {
      super.onCreate();
      Log.w("MyService", "onCreate callback called");
   }

   @Override
   public void onDestroy() {
      super.onDestroy();
      Log.w("MyService", "onDestroy callback called");
   }
}

Next, we will create a Intent service. Right-click the /src tab and select New -> Service -> IntentService

Serv08
Figure 8: Selecting a New Service

Leave the suggested name for the service unchanged and click Finish to create the class.

Serv09
Figure 9: Leaving the suggested service name unchanged

You will see that the default code that is generated by Android Studio has the following content:

package com.example.vipul.servicedemo;

import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.util.Log;

/**
 * An {@link IntentService} subclass for handling asynchronous
 * task requests in a service on a separate handler thread.
 * <p/>
 * TODO: Customize class - update intent actions, extra
 * parameters and static helper methods.
 */
public class MyIntentService extends IntentService {
   // TODO: Rename actions, choose action names that describe tasks that
   // this IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
   private static final String ACTION_FOO = "com.example.vipul.servicedemo.action.FOO";
   private static final String ACTION_BAZ = "com.example.vipul.servicedemo.action.BAZ";

   // TODO: Rename parameters
   private static final String EXTRA_PARAM1 =
      "com.example.vipul.servicedemo.extra.PARAM1";
   private static final String EXTRA_PARAM2 =
      "com.example.vipul.servicedemo.extra.PARAM2";

   /**
    * Starts this service to perform action Foo with the given parameters. If
    * the service is already performing a task this action will be queued.
    *
    * @see IntentService
    */
    // TODO: Customize helper method
   public static void startActionFoo(Context context, String param1,
         String param2) {
      Intent intent = new Intent(context, MyIntentService.class);
      intent.setAction(ACTION_FOO);
      intent.putExtra(EXTRA_PARAM1, param1);
      intent.putExtra(EXTRA_PARAM2, param2);
      context.startService(intent);
   }

   /**
    * Starts this service to perform action Baz with the given parameters. If
    * the service is already performing a task this action will be queued.
    *
    * @see IntentService
    */
    // TODO: Customize helper method
   public static void startActionBaz(Context context, String param1,
         String param2) {
      Intent intent = new Intent(context, MyIntentService.class);
      intent.setAction(ACTION_BAZ);
      intent.putExtra(EXTRA_PARAM1, param1);
      intent.putExtra(EXTRA_PARAM2, param2);
      context.startService(intent);
   }

   public MyIntentService() {
      super("MyIntentService");
   }

   @Override
   protected void onHandleIntent(Intent intent) {
      if (intent != null) {
         final String action = intent.getAction();
         if (ACTION_FOO.equals(action)) {
            final String param1 = intent.getStringExtra(EXTRA_PARAM1);
            final String param2 = intent.getStringExtra(EXTRA_PARAM2);
            handleActionFoo(param1, param2);
         } else if (ACTION_BAZ.equals(action)) {
            final String param1 = intent.getStringExtra(EXTRA_PARAM1);
            final String param2 = intent.getStringExtra(EXTRA_PARAM2);
            handleActionBaz(param1, param2);
         }
      }
    }

   /**
    * Handle action Foo in the provided background thread with the
    * provided parameters.
    */
   private void handleActionFoo(String param1, String param2) {
      // TODO: Handle action Foo
      throw new UnsupportedOperationException("Not yet implemented");
   }

   /**
    * Handle action Baz in the provided background thread with the
    * provided parameters.
    */
   private void handleActionBaz(String param1, String param2) {
      // TODO: Handle action Baz
      throw new UnsupportedOperationException("Not yet implemented");
   }
}

We will implement the other callbacks for the service.

package com.example.vipul.servicedemo;

import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.util.Log;

/**
 * An {@link IntentService} subclass for handling asynchronous
 * task requests in a service on a separate handler thread.
 * <p/>
 * TODO: Customize class - update intent actions, extra parameters
 * and static helper methods.
 */
public classMyIntentService extends IntentService {
   // TODO: Rename actions, choose action names that describe tasks
   // that this IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
   private static final String ACTION_FOO =
      "com.example.vipul.servicedemo.action.FOO";
   private static final String ACTION_BAZ =
      "com.example.vipul.servicedemo.action.BAZ";

   // TODO: Rename parameters
   private static final String EXTRA_PARAM1 =
      "com.example.vipul.servicedemo.extra.PARAM1";
   private static final String EXTRA_PARAM2 =
      "com.example.vipul.servicedemo.extra.PARAM2";

   /**
    * Starts this service to perform action Foo with the given parameters. If
    * the service is already performing a task this action will be queued.
    *
    * @see IntentService
    */
    // TODO: Customize helper method
   public static void startActionFoo(Context context, String param1,
         String param2) {
      Intent intent = new Intent(context, MyIntentService.class);
      intent.setAction(ACTION_FOO);
      intent.putExtra(EXTRA_PARAM1, param1);
      intent.putExtra(EXTRA_PARAM2, param2);
      context.startService(intent);
   }

   /**
    * Starts this service to perform action Baz with the given parameters. If
    * the service is already performing a task this action will be queued.
    *
    * @see IntentService
    */
    // TODO: Customize helper method
   public static void startActionBaz(Context context, String param1,
         String param2) {
      Intent intent = new Intent(context, MyIntentService.class);
      intent.setAction(ACTION_BAZ);
      intent.putExtra(EXTRA_PARAM1, param1);
      intent.putExtra(EXTRA_PARAM2, param2);
      context.startService(intent);
   }

   public MyIntentService() {
      super("MyIntentService");
   }

   @Override
   protected void onHandleIntent(Intent intent) {
      if (intent != null) {
         final String action = intent.getAction();
         if (ACTION_FOO.equals(action)) {
            final String param1 = intent.getStringExtra(EXTRA_PARAM1);
            final String param2 = intent.getStringExtra(EXTRA_PARAM2);
            handleActionFoo(param1, param2);
         } else if (ACTION_BAZ.equals(action)) {
            final String param1 = intent.getStringExtra(EXTRA_PARAM1);
            final String param2 = intent.getStringExtra(EXTRA_PARAM2);
            handleActionBaz(param1, param2);
         }
      }
   }

   /**
    * Handle action Foo in the provided background thread with the provided
    * parameters.
    */
   private void handleActionFoo(String param1, String param2) {
      // TODO: Handle action Foo
      throw new UnsupportedOperationException("Not yet implemented");
   }

   /**
    * Handle action Baz in the provided background thread with the provided
    * parameters.
    */
   private void handleActionBaz(String param1, String param2) {
      // TODO: Handle action Baz
      throw new UnsupportedOperationException("Not yet implemented");
   }

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      Log.w("MyIntentService", "onStartCommand callback called");
      return super.onStartCommand(intent, flags, startId);
   }

   @Override
   public void onStart(Intent intent, int startId) {
      Log.w("MyIntentService", "onStart callback called");
      super.onStart(intent, startId);
   }

   @Override
   public void onDestroy() {
      Log.w("MyIntentService", "onDestroy callback called");
      super.onDestroy();
   }
}

Finally, we will add a couple of buttons on MainActivity. When the user clicks the buttons, we will invoke the services we have created above.

<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:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   android:paddingBottom="@dimen/activity_vertical_margin"
   tools:context=".MainActivity" >

   <TextView android:text="@string/hello_world"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView" />

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Start Service"
      android:id="@+id/buttonStartService"
      android:layout_below="@+id/textView"
      android:layout_toRightOf="@+id/textView"
      android:layout_toEndOf="@+id/textView"
      android:layout_marginTop="62dp"

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Start Intent Service"
      android:id="@+id/buttonStartIntentService"
      android:layout_centerVertical="true"
      android:layout_alignLeft="@+id/buttonStartService"
      android:layout_alignStart="@+id/buttonStartService"
</RelativeLayout>

We will now implement the click event handlers for the buttons:

// MainActivity.java

package com.example.vipul.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;


public class MainActivity extends ActionBarActivity {

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

   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
      // Inflate the menu; this adds items to the action bar
      // if it is present.
      getMenuInflater().inflate(R.menu.menu_main, menu);
      return true;
   }

   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
      // Handle action bar item clicks here. The action bar will
      // automatically handle clicks on the Home/Up button, so long
      // as you specify a parent activity in AndroidManifest.xml.
      intid = item.getItemId();

      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
         return true;
      }

      return super.onOptionsItemSelected(item);
   }

   public void onButtonServiceClick(View view) {
      Intent  myServiceIntent = new Intent(this, MyService.class);
      startService(myServiceIntent);
   }


   public void onButtonIntentServiceClick(View view){
      Intent  myIntentServiceIntent = new Intent(this, MyIntentService.class);
      startService(myIntentServiceIntent);
         
   }
}

Finally, we will wire up the event handlers with the event.

// Activity_main.xml
<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:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   android:paddingBottom="@dimen/activity_vertical_margin"
   tools:context=".MainActivity" >

   <TextView android:text="@string/hello_world"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView" />

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Start Service"
      android:id="@+id/buttonStartService"
      android:layout_below="@+id/textView"
      android:layout_toRightOf="@+id/textView"
      android:layout_toEndOf="@+id/textView"
      android:layout_marginTop="62dp"
      android:onClick="onButtonServiceClick" />

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Start Intent Service"
      android:id="@+id/buttonStartIntentService"
      android:layout_centerVertical="true"
      android:layout_alignLeft="@+id/buttonStartService"
      android:layout_alignStart="@+id/buttonStartService"
      android:onClick="onButtonIntentServiceClick" />
</RelativeLayout>

Our application is now complete. When we run the application and click the buttons, we will see the screen as shown in Figure 10.

Serv10
Figure 10: The screen displaying two buttons

However, when we observe the logcat view with the warning filter, the content in Figure 11 appears:

Serv11
Figure 11: The logical view

You will observe the following order of callbacks being invoked.

11-22 16:50:40.823 1690-1709/com.example.vipul.servicedemo
   W/EGL_emulation : eglSurfaceAttrib not implemented
11-22 16:50:40.823 1690-1709/com.example.vipul.servicedemo
   W/OpenGLRenderer : Failed to set EGL_SWAP_BEHAVIOR on
   surface 0xb4b48380, error=EGL_SUCCESS
11-22 16:51:08.180 1690-1690/com.example.vipul.servicedemo
   W/MyService : onCreate callback called
11-22 16:51:08.183 1690-1690/com.example.vipul.servicedemo
   W/MyService : onStartCommand callback called
11-22 16:51:17.123 1690-1690/com.example.vipul.servicedemo
   W/MyIntentService : onStartCommand callback called
11-22 16:51:17.124 1690-1690/com.example.vipul.servicedemo
   W/MyIntentService : onStart callback called
11-22 16:51:17.182 1690-1690/com.example.vipul.servicedemo
   W/MyIntentService : onDestroy callback called
11-22 16:53:19.340 1690-1697/com.example.vipul.servicedemo
   W/art : Suspending all threads took: 5.497ms
11-22 16:54:37.475 1690-1697/com.example.vipul.servicedemo
   W/art : Suspending all threads took: 5.694ms
11-22 16:57:33.791 1690-1697/com.example.vipul.servicedemo
   W/art : Suspending all threads took: 7.551ms
11-22 16:58:58.447 1690-1697/com.example.vipul.servicedemo
   W/art : Suspending all threads took: 5.555ms

This shows the event order in which service callbacks are called.

You can see that the IntentService destroy callback was destroyed in my case because I stopped app debugging.

Summary

In this article, we learned the basics about Android services and how to create a custom service and a intent service. I hope you have found this information useful. You can download the sample code from the link at the bottom of this article.

About the Author

Vipul Patel is a technology geek based in Seattle. He can be reached at vipul.patel@hotmail.com. You can visit his LinkedIn profile at https://www.linkedin.com/pub/vipul-patel/6/675/508.


Tags: Android, services, Android applications, Click event, callback, Intent service




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel