0

Here's what I'm trying to do: check a certain website periodically, parse the XML and make the phone vibrate if some conditions are met. I have an AsyncTask where I declared an XmlPullParser - I go over the XML tags and once I find the tag I'm interested in, it checks the value and makes the phone vibrate if that value is in the range I want it to be; however I ran into a catch-22 when trying to run it repeatedly.

I was looking at this StackOverflow post and went with that implementation, but here's what happens:

In onStart I have a startTimer function which looks like this:

private synchronized void startTimer(final Context context){
    timer = new Timer();
    TimerTask task = new TimerTask(){
        @Override
        public void run(){
            executing.set(true);
            functionThatExecutesAsyncTask();
        }
    };
    timer.schedule(task, 01, updateFrequency);
}

The first issue I ran into with this was that I had a new timer created each time I would switch to the app (or flipping the screen), each executing the AsyncTask over the updateFrequency. There were also five AsyncTasks (all listed as "WAITING") in the Threads debugging window, as if each timer created a new task rather than the same one; surprisingly, while I had more than 5 timers, I never had more than 5 tasks, for some reason. To solve the issue of multiple timers, I decided to override onStop and get rid of the existing timer (which would also help in case I'm flipping my phone and want the new timer to call the task):

@Override
public void onStop() {
    super.onStop();
    if(timer != null) {
        timer.cancel();
        timer.purge();
        timer = null;
    }
    // ATTENTION: This was auto-generated to implement the App Indexing API.
    // See https://g.co/AppIndexing/AndroidStudio for more information.
    AppIndex.AppIndexApi.end(client, getIndexApiAction());
    client.disconnect();
}

The problem here is obvious: even though additional timers aren't created, I also don't have anything to make the phone vibrate at defined intervals as every single timer is destroyed when the app stops. It also didn't solve the problem of multiple AsyncTasks being created.

Here are the questions:

  • in a different StackOverflow post, someone recommended not using AsyncTask for doing things repeatedly, rather using a thread instead, however that was only one recommendation, while searching for "call AsyncTask repeatedly" yields numerous results. Is AsyncTask not the right thing to use here?

  • how can I only have one AsyncTask that's called each time, rather than five? And why are there five created?

  • how do I avoid the catch-22 of having either an infinity of timers or none at all? I definitely want the app to keep updating even when the screen is switched off and the phone is in the depths of my pocket.

Community
  • 1
  • 1
RockOnRockOut
  • 751
  • 7
  • 18
  • you call `startTimer` in `onStart` only? – Trash Can Jan 22 '17 at 19:12
  • @Dummy - yes. I was a bit conflicted between putting it in `onStart` or `onCreate`, but decided to go with `onStart`. – RockOnRockOut Jan 22 '17 at 19:14
  • `onCreate` is only called when your app is starting "fresh" but not when you resume your app, `onResume` is **always** called no matter what, and `onStart` is invoked when you navigate back to the app after it was stopped(it was in the back ground for a while but still alive) – Trash Can Jan 22 '17 at 19:27
  • @Dummy right, but I thought that onCreate would have been enough - the timer only needs to be created when the application is "fresh" - after that, the same timer can keep calling the task. – RockOnRockOut Jan 22 '17 at 19:47
  • You should use [AsyncTaskLoader](https://developer.android.com/reference/android/content/AsyncTaskLoader.html) instead. – ashkhn Jan 22 '17 at 19:49
  • @RockOnRockOut When you rotate the screen, android will destroy your activity and creates a new one, that's why `onStart` is called – Trash Can Jan 22 '17 at 19:49

1 Answers1

1

If timer is null, we know it does not exist yet, so safe to create a new one. timer is null in these cases:

  • Your app is starting "fresh"
  • Your app was destroyed by android because it was idle(in the background) for awhile.

if (timer == null) { startTimer(this); }

Trash Can
  • 6,608
  • 5
  • 24
  • 38
  • Based on your comments, checking whether there's anything in SharedPreferences for `isTimerCreated` would be done in onCreate rather than onStart, is that correct? Also, would this solve the issue of multiple AsyncTask being created? – RockOnRockOut Jan 22 '17 at 19:55
  • Actually, my answer isn't perfect, because if you app is destroyed, it means your timer object will be too, but the `isTimerCreated` is still true, so in that case, that won't work. Check my new edit. Depends on your use case, we can call it whereever, because you already have this guard `if (timer == null)` it will not call `startTimer` more than once as long as your app isn't destroyed after the `Timer` was created – Trash Can Jan 22 '17 at 20:09
  • So if I understand it correctly: there are three cases where it can be destroyed - when I rotate the phone, when I close the application or when it's been running in the background for a long time. Is that correct? – RockOnRockOut Jan 22 '17 at 20:11
  • @RockOnRockOut Correct – Trash Can Jan 22 '17 at 20:13