Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Asynchronous callback from Service using messaging

4.42/5 (8 votes)
3 Oct 2013CPOL2 min read 41.3K   716  
How to make callback using messaging via LocalBroadcastManager

Introduction

This code shows how to integrate Activities and Services using messaging - not RPC (binding). 

The code simulates a long running process - for example a data synchronization with a WebServer.

The example consists of  an Activity (MainActivity) containing: 

  • A ProgressBar (shoving the service progress)  
  • A TextView (showing the final service result)  
  • A Button (to start the service)   

And a Service (ServerSyncService). When ServerSyncService receives an Intent it performes the following logic: 

  • Loop 1 to 100 
    • Update ProgressBar (via callback)  
    • Sleep 200 ms  
  • Set TextView to "Data from server..." (via callback)  

Callback from the Service is done by sending an Intent to MainActivity via LocalBroadcastManager.

The Intent contains a command and a data part (embedded as putExtras). The commands are

  • COMMAND 
  • DATA 

The command structure is:

Command  Data 
UPDATE_PROGRESS Percent completed  
RESULT  Data from server 
 

To avoid "magic numbers" a helper class "Constant" is used to hold global constants - for example putExtra key names. 

One important feature to notice is that the Activity keeps being updated correctly even if it is rotated (restarted). 

Background  

I teach Android programming, and have been searching for a simple example showing how to make callback from a Service using messaging. I probably did not search enough since I did not find examples I liked Smile | <img src=

Anyway this my self constructed example. The example is based on LocalBroadcastManager 

Using the code

The helper class "Constant"

Java
package dk.eal.kbr.android.example.asyncservicecallbacktoactivity.communication;
 
/**
 * Created by kbr on 01-10-13.
 */
public class Constant
{
    public static final int UPDATE_PROGRESS = 1;
    public static final int RESULT = 2;
    public static final String FILTER = 
      "dk.eal.kbr.android.example.asyncservicecallbacktoactivity.communication.REQUEST_PROCESSED";
    public static final String COMMAND = "COMMAND";
    public static final String DATA = "DATA";
}

MainActivity layout

XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity">
    <ProgressBar
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_gravity="left|center_vertical" />
     <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="@string/result"
        android:id="@+id/textView"
        android:layout_gravity="left|center_vertical" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Service"
        android:id="@+id/button"
        android:onClick="onStartService_Clicked"
        android:layout_gravity="left|center_vertical" />
</LinearLayout> 

MainActivity java code  

Java
package dk.eal.kbr.android.example.asyncservicecallbacktoactivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import dk.eal.kbr.android.example.asyncservicecallbacktoactivity.communication.Constant;
public class MainActivity extends Activity {
    private ProgressBar mProgress;
    private TextView result;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgress = (ProgressBar)findViewById(R.id.progressBar);
        mProgress.setMax(100);
        result = (TextView)findViewById(R.id.textView);
        LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
                new IntentFilter(Constant.FILTER));
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    public void onStartService_Clicked(View v)
    {
        mProgress.setProgress(0);
        result.setText("");
        Intent intent = new Intent(this, ServerSyncService.class);
        startService(intent);
    }
 
    @Override
    protected void onStart() {
        super.onStart();
        LocalBroadcastManager.getInstance(this).registerReceiver((mMessageReceiver), new IntentFilter(Constant.FILTER));
    }
    @Override
    protected void onStop() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
        super.onStop();
    }
    private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("KBR", "Got message");
            handleMessage(intent);
        }
    };
    private void handleMessage(Intent msg)
    {
        Bundle data = msg.getExtras();
        switch (data.getInt(Constant.COMMAND, 0))
        {
            case Constant.UPDATE_PROGRESS:
                int progress = data.getInt(Constant.DATA, 0);
                mProgress.setProgress(progress);
                break;
            case Constant.RESULT:
                String res = data.getString(Constant.DATA);
                result.setText(res);
                break;
            default:
                break;
        }
    }
} 

Important code: 

In the methods  

  • onCreate 
  • onStart 
  • onStop  

it is very important to use the LocalBroadcastManager to start/stop listening for incoming Intents from ServerSyncService. 

When an Intent arrives the method  "handleMessage" is called. "handleMessage" inspects the command part of the message and perform the requested action on the main thread. 

 

ServerSyncService java code 

Java
package dk.eal.kbr.android.example.asyncservicecallbacktoactivity;
import android.app.IntentService;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import dk.eal.kbr.android.example.asyncservicecallbacktoactivity.communication.Constant;
/**
 * Created by kbr on 01-10-13.
 */
public class ServerSyncService extends IntentService {
    public ServerSyncService() {
        super("ServerSyncService");
    }
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        for(int i = 1 ; i <= 100 ; i++)
        {
            sendUpdateMessage(i);
            try
            {
            Thread.sleep(200);
            }
            catch(Exception e)
            {
                Log.d("KBR", "SendError: " + e.getMessage());
            }
        }
        sendResultMessage("Data fra serveren...");
        stopSelf();
    }
    private void sendUpdateMessage(int pct) {
        Log.d("KBR", "Broadcasting update message: " + pct);
        Intent intent = new Intent(Constant.FILTER);
        intent.putExtra(Constant.COMMAND, Constant.UPDATE_PROGRESS);
        intent.putExtra(Constant.DATA, pct);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
    private void sendResultMessage(String data) {
        Log.d("sender", "Broadcasting result message: " + data);
        Intent intent = new Intent(Constant.FILTER);
        intent.putExtra(Constant.COMMAND, Constant.RESULT);
        intent.putExtra(Constant.DATA, data);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
} 

The interesting part of the code is the methods sendUpdateMessage and sendResultMessage. They build the messages for progressbar update and service result respectively.  

Manifest   

XML
 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="dk.eal.kbr.android.example.asyncservicecallbacktoactivity"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="16" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="dk.eal.kbr.android.example.asyncservicecallbacktoactivity.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".ServerSyncService">
        </service>
    </application>
</manifest>

Remember to include the service in the manifest Smile | <img src=

Points of Interest

To me it has been great to dive into a pure messaging way of communication

History

Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)