0

I'm not sure if this is the correct way to go about but I will try and explain what I want to do.

I have an Activity which creates a fragment called TemporaryFragment with a label. What I want to do is create and start a service with a Timer in it and that Timer then updates the time in that TextView.

The way I am thinking of going is somehow, when the Service is started, passing the TextView from the Activity to the Service and then the Service keeping a reference to it.

Another possible way is to make the Activity become a listener of the Service and then calling a method in the Service to update the TextView.

Any thoughts would be great and maybe some options.

Thanks in advance.

ADDITION I'm sorry, I should also specify that I need this timer to run in the background. So when the application is sent to the background, I need the timer to carry on and only stop when I tell it to.

StuStirling
  • 15,601
  • 23
  • 93
  • 150

4 Answers4

1

Service is not ideal for such minor task like this, moreover, Service can be run independently of activity. Also spawning new thread or using timer which introduces new thread into the application is not ideal for this relatively minor reason if you are thinking in the terms of mobile applications.

Instead use Handler in your fragment.

create handler in your fragment

private Handler mHandler = new Handler();

to execute your defined task call

mHandler.postDelayed(mUpdateTask, 1000);

or

mHandler.post(mUpdateTask);

and define your task in the fragment

private Runnable mUpdateTask = new Runnable() {
    public void run() {
        Toast.makeText(getActivity(), "hello world", Toast.LENGTH_SHORT).show();
        mHandler.postDelayed(this, 1000);
    }
};

If you are showing time-like information instead of countdown-like one, use

mHandler.removeCallbacks(mUpdateTimeTask);

in onPause() method to stop executing your task if the activity is not visible as updating UI isn't relevant and it saves battery (you start task again in onResume() method)

biegleux
  • 13,179
  • 11
  • 45
  • 52
  • sorry I have added an addition to my question above – StuStirling Jul 11 '12 at 13:33
  • Still you can use Handler and call removeCallbacks() only in onDestroyView() or onDestroy() method of your fragment. Also in onPause() you can set a flag to not update UI while still execute some counting in your task and in onResume() you set the flag back to update UI. This way your task is execute while the fragment and it's particular view exists and updates it only if is visible. – biegleux Jul 11 '12 at 13:41
  • @biegleux if we apply this solution in context of an activity, if it okay to create a separate Thread for it just to take off some from the main Thread? – H.Karatsanov Jun 04 '20 at 10:50
0

I would use a more simple approach by using a Thread:

public class MainActivity extends Activity implements Callback {
    private static final int MSG_UPDATE = 1;
    private static final long INTERVAL = 1000; // in ms

    private final Handler handler = new Handler(this);
    private Thread worker;

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

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_UPDATE:
                updateView();
                return true;
        }
        return false;
    }

    private void updateView() {
        // TODO tbd
    }

    @Override
    protected void onStart() {
        super.onStart();
        // start background thread
        worker = new Thread(new Runnable() {
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        Thread.sleep(INTERVAL);
                    } catch (InterruptedException e) {
                        break;
                    }
                    // send message to activity thread
                    handler.sendEmptyMessage(MSG_UPDATE);
                }
            }
        });
        worker.start();
    }

    @Override
    protected void onStop() {
        super.onStop();
        // stop background thread
        worker.interrupt();
        try {
            worker.join();
        } catch (InterruptedException e) {
        }
        worker = null;
    }
}
chrulri
  • 1,822
  • 14
  • 16
  • I am going to try and implement this option. One problem, when create the handler `private final Handler handler = new Handler(this);` it wants me to remove the context? – StuStirling Jul 11 '12 at 13:25
  • As you can see in my code example, `this` (`MainActivity`) implements the `Callback` interface (calling the `handleMessage(..)` method) – chrulri Jul 11 '12 at 13:30
  • sorry I have added an addition to my question above – StuStirling Jul 11 '12 at 13:33
  • You cannot update something in an Activity which is already gone (e.g. background). You may continue updating it as soon as the activity is re-startet (e.g. `onStart()`) – chrulri Jul 11 '12 at 13:35
  • so I come from an iOS background and the apps have the ability to continue running in the background. Is that not the case in android, when you go back out of an application thats it? – StuStirling Jul 11 '12 at 13:52
  • Activities are only active as long as they are in foreground. They may be destroyed if they are not used anymore. Read more about this: [here](http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle) But this does not mean that your application gets terminated. You are able to run more stuff in background using a `Service`. BUT, it does not make any sense to update UI stuff (e.g. Views in an activity) which is not visible/available to the user. As I said, you may update the UI when the Activity is re-started. What is your goal? – chrulri Jul 11 '12 at 13:59
  • Basically, the idea behind the timer is eventually I am going to add some tracking into my application and therefore need it to continue running even if the application isn't in the foreground – StuStirling Jul 11 '12 at 14:09
  • Well this is definitly another use case. See my other answer. – chrulri Jul 11 '12 at 14:21
0

You can use the TimerTask Class for this. Override the TimerTask.run() method and then add that TimerTask to Timer class.

Also check this question: controlling a task with timer and timertask

Community
  • 1
  • 1
Sats
  • 875
  • 6
  • 12
0

Basically, the idea behind the timer is eventually I am going to add some tracking into my application and therefore need it to continue running even if the application isn't in the foreground – Disco S2

Based on this comment I suggest you to use a local service which resides in the background, doing it's stuff (start a thread from Service#onStart), until it gets stopped by stopService(..).

Activities on the other hand may bind and unbind to that service (see: bindService(..)) to get notified about updates or to communicate with the service in any way.

chrulri
  • 1,822
  • 14
  • 16
  • would i need to bind or just start my service. I do probably need to access it again whilst its running so I'm thinking I need to bind. However, does that mean it will still run when the application is sent to background? – StuStirling Jul 11 '12 at 14:32
  • You need to start the service with `START_STICKY` as return value in `onStartCommand(..)`. Start it by any activity or on application start using `Context#startService(..)` and stop it whenever you want with `Context#stopService(..)` whereas your activities can bind and unbind as often as you want. reference: http://developer.android.com/reference/android/app/Service.html#ServiceLifecycle – chrulri Jul 11 '12 at 14:34
  • I have got it working and comunicating with the activity by passing the service a handler and then send a message back – StuStirling Jul 11 '12 at 15:42