Showing posts with label Service. Show all posts
Showing posts with label Service. Show all posts

Thursday, 26 October 2017

Testing Service lifecycle method calls

Let's suppose you have created a local Service (implementing the Local Binder Pattern) to perform some tasks for your app, delivering the results back to a connected Activity. 
It is important to properly test the Service to make sure it behaves as you expect. The first thing to do, as the Android documentation suggests, is testing the Service's lifecycle methods.

To be more precise:
  • ensure that the onCreate() is called in response to Context.startService() or Context.bindService(). Similarly, you should ensure that onDestroy() is called in response to Context.stopService(), Context.unbindService(), stopSelf(), or stopSelfResult(). Test that your Service correctly handles multiple calls from Context.startService(). Only the first call triggers Service.onCreate(), but all calls trigger a call to Service.onStartCommand()
If you are not familiar with testing Services you can first check the official Android documentation here:
Testing Your Service


But how do you do that without mixing the code of your Service, implementing you business logic, with the code needed to keep track of the method calls?

@VisibleForTesting

The @VisibleForTesting annotation indicates that an annotated method (or variable) is more visible than normally necessary to make the method testable. This annotation has an optional otherwise argument that lets you designate what the visibility of the method should have been if not for the need to make it visible for testing. Lint uses the otherwise argument to enforce the intended visibility.

You can also specify @VisibleForTesting(otherwise = VisibleForTesting.NONE) to indicate that a method exists only for testing. This form is the same as using @RestrictTo(TESTS). They both perform the same lint check.
For our purpose we can define some static variables, inside our Service, used to keep track of the lifecycle method calls. These variables are annotated with @VisibleForTesting, meaning that they are only used for testing purposes.
// Just for testing.
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onCreateCalls = 0;
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onDestroyCalls = 0;
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onStartCommandCalls = 0;
In the relevant Service lifecycle methods we increment our counters:
@Override
public void onCreate() {
    Log.d(TAG, "RecordingService - onCreate");
    onCreateCalls++;
    // ...
}

@Override
public void onDestroy() {
    Log.d(TAG, "RecordingService - onDestroy");
    onDestroyCalls++;
    // ...
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStartCommandCalls++;
    // ...
}

You can see the whole class on GitHub (it's a hybrid Service, both bound and started, used to record audio with a MediaRecorder):
RecordingService.java

Testing the Service

Testing our Service is now very easy. We first define the ServiceTestRule as follows:
@Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule() {
    @Override
    protected void afterService() {
        super.afterService();           
        RecordingService.onCreateCalls = 0;
        RecordingService.onStartCommandCalls = 0;
        RecordingService.onDestroyCalls = 0;
    }
};
Note that in the overriden afterService method, called when the Service is shut down after each test, we reset our counters to 0.

Testing the lifecyle method calls is now very easy:
/*
    Test that the Service's lifecycle methods are called the exact number of times in response
    to binding, unbinding and calls to startService.
*/
@Test
public void testLifecyleMethodCalls() throws TimeoutException {
    // Create the service Intent.
    Intent serviceIntent = RecordingService.makeIntent(InstrumentationRegistry.getTargetContext(), true);

    mServiceRule.startService(serviceIntent);
    IBinder binder = mServiceRule.bindService(serviceIntent);
    RecordingService service = ((RecordingService.LocalBinder) binder).getService();
    mServiceRule.startService(serviceIntent);
    mServiceRule.startService(serviceIntent);

    assertNotNull("Service reference is null", service);
    assertEquals("onCreate called multiple times", 1, RecordingService.onCreateCalls);
    assertEquals("onStartCommand not called 3 times as expected", 3, RecordingService.onStartCommandCalls);

    mServiceRule.unbindService();
    assertEquals("onDestroy not called after unbinding from Service", 1, RecordingService.onDestroyCalls);
}

You can have a look at the entire test class here:
RecordingServiceTest.java

Wednesday, 12 March 2014

Binding Services – part 3 (communication Service -> Activity)

In our previous tutorial about IntentService we saw that a BroadcastReceiver can be used as a means of communication between the Service and the calling client. Hower this method cannot be used for frequent and fast calls, such as for progress updates. For this purpose we have to use a different approach...
For more information about binding Services and the way they are connected to a calling Activity, please refer to my previous tutorials.


SERVICE

First of all we have to define a public interface inside our Service as a means of communication between the Service and the calling Activity:

public interface InterfaceCallback {
   void onProgressUpdate(int progress);
   void onTaskCompleted(MyResult myResult);
}

where MyResult is a user defined class carrying the results of the operation performed by the Service in the background.
The next step is to connect our Service to the calling Activity. To do this, we define an instance variable of the type InterfaceCallback and create a public method that will be called by the calling Activity to connect itself to the Service (the following code goes inside the Service class):

private InterfaceCallback interfaceCallback;
...
public void setInterfaceCallback(InterfaceCallback interfaceCallback) {
   this.interfaceCallback = interfaceCallback;
}

Now our Service is ready to communicate with the calling Activity using the interfaceCallback reference. For example, if we want to notify the updates of a background task (from the onProgressUpdate method of an AsyncTask, for instance) we can call:

interfaceCallback.onProgressUpdate(progress);

In the same way, if we need to communicate that the background operation has been completed and send and object containing the results, we can simply call:

interfaceCallback.onTaskCompleted(myResult);




CALLING ACTIVITY

To make sure that our Activity can receive updates from the Service we have to implement the previously defined interface:

public class CallingActivity extends Activity implements ServiceConnection, MyService.InterfaceCallback {
//see previous tutorial
}

Once we have established a valid connection with the Service, we can register our Activity as a callback (our Activity overrides the InterfaceCallback methods that are triggered by the Service):

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
  MyService myService = ((MyService.MyBinder) iBinder).getReference();
   myService.setInterfaceCallback(this);
myService.startBackgroundTask();
}

@Override
protected void onPause() {
   super.onPause();
   ...
   if(myService != null) {
      myService.setInterfaceCallback(null);
      unbindService(this);
   }
}

Finally we have to override the methods onProgressUpdate and onTaskCompleted of the implemented InterfaceCallback interface. These methods, as we already saw, are called from the Service to notify the connected Activity about the progress updates and the completion of the background task:

@Override
public void onProgressUpdate(int progress) {
   //code left out for brevity
}

@Override
public void onTaskCompleted(MyResult myResult) {
   //code left out for brevity
}


to be continued...

Wednesday, 5 March 2014

Binding Services - part 2 (staying in the foreground)

In our previous tutorial we saw how to create a binding Service and how to bind to it using the Binder pattern. This is a very useful pattern because, once we retrieve a valid reference to our Service, we can use this reference to call Service's methods like any other Java object.



Binding Services usually stay alive until the last client disconnects, calling the unbindService method. When no more clients are connected, the Service is no longer running in the foreground and may be targeted by the system for shutdown (usually when Android is running out of resources).
In order to prevent the system from doing this, we can use the startForeground method. This method makes sure that the Service will stay in the foreground state (usually when executing long running tasks in the background) even if no client is connected.
The only thing to remember is to always call stopForeground, with the parameter true, when the Service has completed its task, otherwise we could potentially waste system resources.



Let's suppose to define a method in our Service class called executeTask, which is meant to perform long running operations. This method is called from our Activity, once a valid reference to our Service is obtained (see previous tutorial for more details), with the following code:

myService.executeTask();

Now we want that our Service will stay in the foreground until the task is completed. We also create a notification reporting that a background operation is running:

public void executeTask() {
   //create the Notification
   Notification.Builder builder = new Notification.Builder(this);
   builder.setContentTitle("Notification title");
   builder.setContentText("Notification text");
   builder.setSmallIcon(R.drawable.icon_for_notification);
   Notification mNotification = builder.build();

   //the Service won't be stopped by the system
   startForeground(1001, mNotification);

  //background operations: left out for brevity
  ...

  //remove the Service from foreground state
  stopForeground(true);
}

Monday, 24 February 2014

Binding Services – part 1

In this tutorial I’ll explain the basics of binding Services. For more information about what Services are and what they are used for please refer to my previous tutorial, IntentServices in Android, that you can find here.


Services in Android can be activated in two ways:
  • you can start a Service through the Context.startService() method;
  • or you can bind to a Service through the Context.bindService() method.

The main difference is that if you bind to a Service, the Service will stay alive until the last client disconnects. In addition to that, if you bind to a Service, instead of starting it, you have a reference to the Service class that can be used to easily call the Service’s methods.
In this first tutorial about binding Services we’ll see how you have to set up your Service class to allow other components to be able to bind to it. To do this you have to implement the onBind method of the Service class, which must return an IBinder object.
In the following example we create an inner class, called MyBinder (extending the Binder class – please note that the Binder class implements the IBinder interface), and we return an object of this class in the onBind method (we can do so because, as already said, the Binder class implements the IBinder interface).

In our MyBinder class we create a method returning a reference to our Service class, which is used in the clients binding to the Service:

public class MyService extends Service {
   //the content of the Service is omitted for brevity
   public void startBackgroundTask() {
      ...
   }
   ...

   //MyBinder class extending Binder
   public class MyBinder extends Binder {
      public MyService getReference() {
         return MyService.this;
      }
   }

   //create an instance of our MyBinder class
   private MyBinder myBinder = new MyBinder();

   //onBind method, returning an instance of our MyBinder
   @Override
   public IBinder onBind(Intent intent) {
return myBinder;
}
}

Now that we have implemented the onBind method in our Service, we can easily bind to the Service through the Context.bindService method.
To begin with, our Activity (or other component binding to the Service) must implement the ServiceConnection interface. The ServiceConnection interface provides a callback method, onServiceConnected, called when a valid connection to the Service is established.

public class MyActivity extends Activity implements ServiceConnection {
...
}

To bind to MyService we can use the following code, typically in the onResume method (this refers to the component, our Activity in this example, that implements the ServiceConnection interface):

Intent intent = new Intent(this, MyService.class);
bindService(intent, this, BIND_AUTO_CREATE);

To unbind from MyService the code is the following (typically in the onPause method):

unbindService(this);

Finally we have to override the onServiceConnected and onServiceDisconneted methods of the ServiceConnection interface. These methods are called, respectively, when MyService is connected/disconnected to/from our Activity.

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
   MyService myService = ((MyService.MyBinder) iBinder).getReference();
   myService.startBackgroundTask();
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
   ...
}

As you can see in the onServiceConnected method we have an IBinder parameter, of the type MyService.MyBinder, because in the onBind method of our Service we return an object of this type (MyService.MyBinder extends Binder and implements the IBinder interface).
Using the iBinder reference, casting it to MyService.Binder and calling the method getReference we obtain a reference to our MyService class. You can then use this reference to call specific methods of MyService, omitted here for brevity, to perform background operations (you have of course to use different threads to perform background tasks, because all methods of the Service class run in the UI thread).

To be continued...

Thursday, 16 January 2014

IntentService in Android

In this tutorial I will explain briefly what an IntentService is and how it should be used in an Android app.
To begin with, let's start with a FAQ...


What is a Service?
An Android app is made of components. The most important components are the following: Activity, Service, BroadcastReceiver and ContentProvider.
An IntentService is a special kind of Service and therefore a component of an Android app.

What are Services used for?
Services in Android are generally used for operations that are not directly involved with the user interface (UI). For example: network operations, writing and reading data to/from the local storage (a database, file, etc.), playing music, and other tasks like these.
It is generally better to use one Service for each different task: one Service for network operations, one different Service for managing the database and so on.
Some ot these tasks can also be managed within a normal Activity, using a dedicated thread (AsyncTask or Handler, as I explained in a different tutorial), especially if the data involved comes from the user interface (for example: writing to a local database the data entered by the user).
But there are other tasks (like network operations or playing music) that are bettere managed by Services.
Generally speaking Services allow you to efficiently separate the logic of background tasks from the code that handles the user interface.

Are Services running in a different thread?
No, the code in a Service runs in the main thread (the thread of the GUI). So it is very important to create a different thread within the Service (using an AsyncTask or Handler) to manage long running or slow operations, otherwise they could slow down or block the user interface.

So, what is an IntentService in the end?
An IntentService is a special utility class which represents a Service whose operations run in a different thread than the thread of the GUI.
This is managed automatically by the IntentService class, so you don't have to implement an AsynckTask or a Handler.


Let's see an example.
The following IntentService is used to upload a text or a photo to a remote server in a different thread.

public class MyIntentService extends IntentService {

    //constants
    private static final String CLASSNAME = "MyIntentService";

    //the following 2 actions have to be added in the intent filter for the
    //IntentService in the manifest file
    public static final String ACTION_UPLOAD_TEXT = "com.tutorial.UPLOADTEXT";
    public static final String ACTION_UPLOAD_PHOTO = "com.tutorial.UPLOADPHOTO";
    
    //extras to put in the Intent that starts this IntentService
    public static final String TEXT = "text";
    public static final String PHOTO= "photo";

    public MyIntentService() {
        super(CLASSNAME);

        //if the system shuts down the service, the Intent is not redelivered
        //and therefore the Service won't start again
        setIntentRedelivery(false);
    }

    //this method runs in a different thread (not the main thread)
    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction();

        if(action.equals(ACTION_UPLOAD_TEXT)) {
            String text = intent.getStringExtra(TEXT);
            uploadText(text);
        }
        else if(action.equals(ACTION_UPLOAD_PHOTO)) {
            Bitmap photo = itent.getParcelableExtra(PHOTO);
            uploadPhoto(photo);
        }
    }

    private void uploadText(String text) {
        //in this method you upload the text to the server: omitted for brevity
    }

    private void uploadPhoto(Bitmap photo) {
        //in this method you upload the photo to the server: omitted for brevity
    }
}

If you want to use this IntentService from your Activity to upload a text or a photo to the remote server in a dedicated thread, you just have to use the command Context.startService() with the Itent carrying the specific action and extras.

There is only one disadvantage in this approach: if you have multiple calls to the IntentService, these will be put in a queue and processed one at a time (there is no parallel execution). If you need parallel execution, to make sure that each background operation is executed as soon as possibile, you have to use a different approach, but this will be the object of a different tutorial.