12

I am creating an application that needs to update values every minute even if the app isn't running.

Of course, I have set up a simple Service to do that. I have debug messages set up to tell me when the Service starts, when it updates (every minute), and when it closes. I also have a message telling me when the values update inside a runOnUiThread() method. All of my messages activate except for the one in the runOnUiThread(). Is there something I'm doing wrong (of course there is)? What do I need to change?

Code:

@Override
public void handleMessage(Message message) {

    try {

        if (!serviceStarted) {

            serviceStarted = true;
            serviceTest = true;

            while (serviceStarted) {

                new MainActivity().runOnUiThread(new Runnable() {

                    public void run() {

                            OverviewFragment.refresh(getApplicationContext());
                            System.out.println("yay");

                         }
                });

                Thread.sleep(((1 /* minutes */) * 60 * 1000));
                System.out.println("Updated values through service.");

            }
        }


    } catch (InterruptedException e) {

        Thread.currentThread().interrupt();
        e.printStackTrace();

    }

    stopSelf(message.arg1);
}
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
Benjamin Owen
  • 608
  • 2
  • 10
  • 28
  • A service already runs on the UI thread, try without the runnable wrapper. Also, that sleep will block your UI. – Steven Trigg Mar 11 '16 at 00:52
  • `runOnUiThread` only works in the visible activity, you can't create a `new` instance of your `Activity` and call the method there. Try sending events from the service and observing them in the **visible** activity (already created with an intent), to can do what you need in activity when something happened in the service. – IgniteCoders Oct 19 '17 at 05:48

4 Answers4

20

So there's no need to do that, unless you're creating a Thread inside of it

Gabe Sechan's answer is correct.

But if you are using a separate thread then instead of following code:

new MainActivity().runOnUiThread(new Runnable() {
       public void run() {
            OverviewFragment.refresh(getApplicationContext());
                 System.out.println("yay");

       }
});

Try, this code:

new Handler(Looper.getMainLooper()).post(new Runnable() {
       public void run() {
            OverviewFragment.refresh(getApplicationContext());
                 System.out.println("yay");
       }
});

As per Android docs

Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise).

3

You can't create an Activity by calling new. It doesn't initialize properly that way.

Also, Services by default run on the UI thread. So there's no need to do that, unless you're creating a Thread inside of it. If you are- runOnUIThread is just syntactic sugar for posting a runnable to a handler. So you can just do that instead.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • 1
    "runOnUIThread is just syntactic sugar for posting a runnable to a handler" -- actually, that's only for a `Handler` attached to the main application thread. You can have a `Handler` attached to a background thread, such as via `HandlerThread`. – CommonsWare Mar 11 '16 at 00:53
  • When I remove the runOnUiThread(), I get this: "Only the original thread that created a view hierarchy can touch its views." :/ – Benjamin Owen Mar 11 '16 at 01:04
  • 3
    Well, yes. You shouldn't ever be touching views in a service anyway- the point of a service is not to have a UI. If you need to update the UI, you should be informing the Activity of the new data and let it update its UI itself. – Gabe Sechan Mar 11 '16 at 01:04
2

Try using a handler or LocalBroadcastManager to send a message to the activity.

Undo
  • 25,519
  • 37
  • 106
  • 129
William
  • 141
  • 5
0

See this question: Accessing UI thread handler from a service

You can use Looper.getMainLooper() within a Handler to post a Runnable that executes whatever you're trying to execute.

A good alternative though, like jinghong mentioned, is to use broadcasts - in other words, use a different pattern.

Community
  • 1
  • 1
drhr
  • 2,261
  • 2
  • 17
  • 35