How can I update information in an Android Activity from a background Service
Asked Answered
H

5

40

I am trying to create a simple Android application that has a ActivityList of information, when the application starts, I plan to start a Service that will be constantly calculating the data (it will be changing) and I want the ActivityList to be in sync with the data that the service is calculating for the life of the app.

How can I set up my Activity to be listening to the Service? Is this the best way to approach this problem?

For example, if you imagine a list of stock prices - the data would be being changed regularly and need to be in sync with the (in my case) Service that is calculating/fetching the data constantly.

Thanks in advance

Hujsak answered 18/3, 2010 at 10:4 Comment(0)
N
96

How can I set up my Activity to be listening to the Service? Is this the best way to approach this problem?

You have three major options, as I see it:

  1. Polling. The Activity periodically asks the Service for the latest data. IMHO, this option sucks, but it's certainly possible.

  2. Callbacks. Per jax's answer, the Activity registers a callback object ("observer") with the Service. The Service invokes a method on the callback when the data changes, which in turn updates the UI. You can see an example of using that with a Service here.

  3. Broadcast Intents. The Service broadcasts an Intent via sendBroadcast() on a data change. The Activity registers a BroadcastReceiver using registerReceiver(), and that BroadcastReceiver is notified of an incoming broadcast. This triggers the Activity to load the latest data from the Service, or possibly just to get the latest data out of extras in the broadcast Intent. You can see an example of using that technique with a Service here.

Nursling answered 18/3, 2010 at 12:10 Comment(9)
Great example of number 3 here ... websmithing.com/2011/02/01/…Tingle
What if i closed the application. How can i handle the case of running AlarmManager with Status Activity.Wilhelmstrasse
What are the pro and cons of techniques 2 and 3?Humidify
@resus: #2 requires binding, which introduces more "state" and makes activity configuration changes more challenging. #3, as written, is truly broadcast to the whole device. In the three years since this answer was written, other options have been added. I would recommend that you consider LocalBroadcastManager from the Android Support package (like #3, but private to your app) or using the Otto event bus: square.github.io/ottoNursling
@CommonsWare: Perhaps would it be good to update the answer with those new infos? After exploring LocalBroadcastManager solution I ended up using the old observer pattern #2. I would add for #2: Easier to exchange complex obects as they don't need to be serializable or parcelable.Humidify
@Nursling any performance differences between #2 and #3 when I send commands from service to activity 20++ times per second? I guess "new" LocalBroadcastManager is no different regarding performances than older one..Hasp
@Hasp The LocalBroadcastManager is more effecient, as the intents are only sent internally and not globally like the old method. Quote from BroadCastReceiver documentation: "...This will give you a much more efficient implementation (no cross-process communication needed) "...Cilurzo
@Nursling could you please update the link to option 3 ? seems like its brokenCountenance
But if I want to update song progress continuosly .which technique is best. I think using broadcast will slow down.Boarfish
I
4

This sound like a good candidate for the Observer Pattern. Basically your activity (The Observer) will register itself with the background service (The Observable) and you can push or pull data from your Activity. Your Observer in this case will be your Activity and the Observable will be your Service.

If you know nothing about Design Patterns buy "Head First Design Patterns", it is easy to read and is full of great information.

PS: I am reading it now.

Inequity answered 18/3, 2010 at 10:33 Comment(0)
C
2

I am really, really wondering why no one mentioned a simple approach using an EventBus by whatever library. This is of course if you are not using RX. My personal favorite is EventBus by GreenRobot. https://github.com/greenrobot/EventBus

With just a couple of lines of code, and no interfaces. Fire an event, and listen for it wherever you want. It is decoupled, it is thread safe, and it will not crash your app.

Cairn answered 8/3, 2017 at 21:0 Comment(0)
H
1

You need to use bindService() to bind the activity with running service and communicate with it.

public class BindingActivity extends Activity {
YourService mService;
boolean mBound = false;

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

@Override
protected void onStart() {
    super.onStart();
    // Bind to Your Service
    Intent intent = new Intent(this, YourService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
    super.onStop();
    // Unbind from the service
    if (mBound) {
        unbindService(mConnection);
        mBound = false;
    }
}

/** Called when a button is clicked (the button in the layout file attaches to
  * this method with the android:onClick attribute) */
public void onButtonClick(View v) {
    if (mBound) {
        // Call a method from your Service.
        // However, if this call were something that might hang, then this request should
        // occur in a separate thread to avoid slowing down the activity performance.
        int num = mService.getRandomNumber();
        Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
    }
}

/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className,
            IBinder service) {
        // We've bound to the running Service, cast the IBinder and get instance
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        mBound = false;
    }
 };
}

and your service like:

public class LocalService extends Service {
    // Binder given to clients
   private final IBinder mBinder = new LocalBinder();
   // Random number generator
   private final Random mGenerator = new Random();

/**
 * Class used for the client Binder.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with IPC.
 */
public class LocalBinder extends Binder {
    LocalService getService() {
        // Return this instance of LocalService so clients can call public methods
        return LocalService.this;
    }
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

/** method for clients */
public int getRandomNumber() {
  return mGenerator.nextInt(100);
  }
}
Hypogeous answered 20/3, 2015 at 13:31 Comment(1)
This isn't quite what the OP asked for. While this could work (the Service continuously updates the data and the UI can ask for the new data), it requires a call from the UI side as a reaction to user input (like a button press). The question on the contrary asks for a way for the UI to continuously update with the new data in the Service.Whereat
S
0

You will have a background thread running that calculates the changes in the list. This thread now needs a possibility to notify the GUI that the list was updated.

You can use some kind of ArrayAdapter to get the data into the ListView. The ArrayAdapter has a method called adpater.notifyDataSetChanged() every time you call this method the adapter will see that the corresponding data has changed and then notify the listview that it should update at the next possibility.

Staats answered 18/3, 2010 at 10:51 Comment(1)
Better yet, if you are going to use ArrayAdapter, just call the add(), insert(), and remove() methods on ArrayAdapter to change its contents. You do not need notifyDataSetChanged() then.Nursling

© 2022 - 2024 — McMap. All rights reserved.