Using ResultReceiver to communicate with IntentService

Bakhtar Sobat
ProAndroidDev
Published in
5 min readJun 30, 2017

--

A Service is an application component that can perform long-running operations in the background, and it does not provide a user interface. A service can be started by other components, e.g. by an Activity.

So when an Activity starts a Service for a background task, how does the activity get the result from the Service? One way to do that is to use the ResultReceiver pattern. In this article I am going use a very simple app to show this by example. I will also use an IntentService, which is a special case of Service component.

IntentService

Before we continue, let’s refresh our memories about IntentServices. An IntentService is a base class for Services that handles asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. All requests are handled on a single worker thread — they may take as long as necessary (and will not block the application’s main loop), but only one request will be processed at a time.

To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

To sum up:

  • The IntentService runs on a separate worker thread.
  • The IntentService is triggered using an Intent, it spawns a new worker thread and the method onHandleIntent() is called on this thread.
  • The IntentService cannot run tasks in parallel. Hence all the consecutive Intents will go into the message queue for the worker thread and will execute sequentially.
  • The IntentService stops the service after all start requests have been handled, so you never have to call stopSelf().

ResultReceiver is a generic interface for receiving a callback result from someone. The ResultReceiver class is just a simple wrapper around a Binder that is used to perform the communication. An instance of this class can be passed through an intent. Use this by creating a subclass and implementing onReceiveResult. The sender uses the send method to send the data to the receiver.

BankDroid

We are going to build a very simple app (BankDroid) to explain this communication. This banking app has 3 features:

  1. Retrieve savings account balance
  2. Deposit (10 euro) to your savings account
  3. Withdraw (10 euro) from your savings account

The app contains two components: 1) MainActivity: a user-interface. This Activity uses the 2) BankService for executing the background tasks. The BankService connects to the backend (in this demo app we don’t use a real backend), executes the tasks and sends the result back to the MainActivity.

BankResultReceiver

Let’s start with the ResultReceiverCallBack.

public interface ResultReceiverCallBack<T>{
public void onSuccess(T data);
public void onError(Exception exception);
}

This callback will be used to pass the result of a background task to the caller. In case of a successful result the onSuccess method will be called and the data will be passed, otherwise the onError method will be called and the exception will be passed.

Now let’s implement our ResultReceiver

We have to override the onReceiveResult(int resultCode, Bundle resultData) method. If the result code is successful (RESULT_CODE_OK), we call the onSuccess method of ResultReceiverCallBack and pass the data, otherwise the onError method will be called.

BankService

Let’s implement the BankService, which extends the IntentService. All we have to do is to override the onHandleIntent method. This method gets an Intent as a parameter, which is sent by the starter of BankService. The Intent contains an action, which tells us what to do, a resultReceiver and additional parameters.

The BankService gets an instance of ResultReceiver through an Intent, along with other parameters to perform a network task. For this example we just wait for a few milliseconds to simulate the network latency.

The BankService uses the ResultReceiver’s send(int, Bundle) method to deliver the result to the receiver.

MainActivity

This is our user-interface. It has a TextView and two buttons. This component starts the BankService and passes an implementation of our BankResultReceiver. The implementation is straightforward:

As mentioned before, The ResultReceiver class is just a simple wrapper around a Binder that is used to perform the communication. This means semantically you should treat it as such: this class does not impact process lifecycle management.

Usually, one can simply create an anonymous implementation of ResultReceiverCallBack that may hold an implicit reference to an enclosing class (in our case the MainActivity), which may create memory leaks. For example, the following example is a good candidate for leaking memory:

BankService.startServiceForBalance(this, new ResultReceiverCallBack<String>() {
@Override
public void onSuccess(String data) { }
@Override
public void onError(Exception exception) { }

});

For this reason, we create static inner classes implementing the callback interface and pass the MainActivity instance as a param that will be kept in a WeakReference.

Please notice that the IntentService cannot run tasks in parallel. Hence all the consecutive Intents will go into the message queue for the worker thread and will execute sequentially.

In the example below:

   BankService.startServiceToDeposit(this, 10, new TransferMoneyResultReceiver(this, true));

BankService.startServiceForBalance(this, new AccountInfoResultReceiver(this));

because we are using an IntentService, the startServiceForBalance will be executed after it is finished with the previous task (startServiceToDeposit).

Conclusion

A ResultReceiver is making it easier to get the result from a (Intent)Service. It is an interface we implement and pass it to the IntentService through an Intent. IntentService will then fetch this object and call its receiver.send function to send anything (in Bundle) to calling Activity.

You can find the source at GitHub: https://github.com/BakhtarSobat/BankDroid

--

--