33

I have an Android service that starts and maintains a background thread.

From time to time, the background thread needs to do a callback on the main thread. I'm stumped as to how to do this.

I can't call this.runOnUiThread because "this" is an instance of Service, not Activity, and a Service doesn't have the runOnUiThread method.

I also can't create or execute an AsyncTask, because the documentation for AsyncTask says that both the constructor and the execute method must be invoked from the UI thread.

Do I need to maintain a reference to the activity that is using the service and call its runOnUiThread method, or is there another way to run something on the UI thread?

Thanks.

Frank LaRosa
  • 3,533
  • 6
  • 26
  • 32
  • 3
    "the background thread needs to do a callback on the main thread" -- why? – CommonsWare Jun 25 '12 at 23:32
  • The user of the service is an activity. It needs callbacks on the main thread so it can update UI elements. I suppose I could call it back on the background thread and make it responsible for scheduling a UI thread callback, but it would be more convenient for the consumer of the service if I could do it. – Frank LaRosa Jun 25 '12 at 23:34
  • 3
    @Cruceo: Oh, heavens no. – CommonsWare Jun 25 '12 at 23:46

5 Answers5

78

I'm using following code from time to time if I do not hold direct access to Activity (for a reason or another);

new Handler(Looper.getMainLooper()).post(mYourUiThreadRunnable);
harism
  • 6,011
  • 1
  • 36
  • 31
  • 4
    I thought it may be outdated, but still in late 2015 this is the recommended way: http://developer.android.com/training/multiple-threads/communicate-ui.html – gaborsch Dec 18 '15 at 19:15
7

For Kotlin:

Handler(Looper.getMainLooper()).post { 
    /*My task*/ 
}
Stoica Mircea
  • 782
  • 10
  • 22
2

If you code in Kotlin you can use coroutine with Main dispatcher:

private fun runOnUiThread(block: () -> Unit) {
    CoroutineScope(Dispatchers.Main).launch { block.invoke() }
}

Of-cause coroutines should added to your project as a dependency.

Daniel
  • 2,415
  • 3
  • 24
  • 34
1

Your activity has to can bind to the service.

http://developer.android.com/guide/components/bound-services.html

Specifically, take a look at creating a Messenger on that page. The client activity can give a messenger object that responds to messages from the service, and once received, run whatever UI code is necessary on the UI thread using a handler.

DO NOT keep the activity's reference in the service. This can lead to all sorts of memory issues.

you786
  • 3,659
  • 5
  • 48
  • 74
  • While `Messenger` is a fine solution, you do not have to bind to the service to use it. `Messenger` is `Parcelable` and can be sent in an `Intent` extra in a command sent via `startService()`. – CommonsWare Jun 25 '12 at 23:48
1

Sure. See Handler. You can give to your service a handler object and when service needs to run some Runnable task on UI thread just must call handler.post(some_runnable_task). This call. Can find a example in this link 4.Tutorial: Handler.