0

From the Activity, I am creating a Handler to fire off my AsyncTask every 45 seconds in order to refresh the content of my ListView's DataAdapter. The AsyncTask works great and keeps the user informed on the progress through ProgressUpdates and Toast messages.

Since the thread's doInBackground is fire and forget and not re-usable, I am having to create a new instance of the AsyncTask from my Hander that is firing off every 45 seconds. The problem is when the screen is rotated and and then I get concurrent messages going off because the Hander was recreated and created a new instance of the AsyncTask, so the friendly user progress through ProgressUpdates and Toast messages is overwhelming and makes utilizing the ListView difficult.

And please don't suggest this as a solution: android:screenOrientation="portrait" is not an option.

For something that has to run so frequently, should I just be using a custom Thread and not the AsyncTask class? ToDo: Not shown, I have to update the Adapter later from the Sensor's onSensorChanged event to update bearings on for each location in the ListView, I was going to run that on a separate AsyncTask class because I don't need to notify the user everytime the device bearing has changed.

Since the AsyncThread cannot be reused, am I doing this all wrong? In short, what is the best way to have the Activity refresh the ListView and keeping off the UI thread when doing so?

antoniom
  • 3,143
  • 1
  • 37
  • 53
CampbellGolf
  • 812
  • 12
  • 27

2 Answers2

1

The problem is when the screen is rotated and and then I get concurrent messages going off because the Hander was recreated and created a new instance of the AsyncTask.

Reason quoting from API Activity - Configuration Changes:

Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate.

So every object has a activity-scope life cycle (i.e. Handler, AsyncTask and etc. defined within your activity class) is suffered by this activity recreation. However, you can bypass this activity recreation, as stated in the later paragraph of Activity - Configuration Changes section:

In some special cases, you may want to bypass restarting of your activity based on one or more types of configuration changes. This is done with the android:configChanges attribute in its manifest. For any types of configuration changes you say that you handle there, you will receive a call to your current activity's onConfigurationChanged(Configuration) method instead of being restarted. If a configuration change involves any that you do not handle, however, the activity will still be restarted and onConfigurationChanged(Configuration) will not be called.

Not related to topic, but as a good practice, you should always destroy used object (Handler, AsyncTask and etc.) properly when activity is about to finish (i.e. in onDestroy() method).

For something that has to run so frequently, should I just be using a custom Thread and not the AsyncTask class?

AsyncTask is pretty handy but not suit for periodic task, I would use ScheduledExecutorService or TimerTask in this case, check out my answer here for sample code.

Community
  • 1
  • 1
yorkw
  • 40,926
  • 10
  • 117
  • 130
  • Thanks York, after further reading, I'm not using the best threading model for this task and find that the Timer will be more suitable for what I'm trying to accomplish. This was the primary reference source for the AsyncTask: http://www.codeproject.com/Articles/162201/Painless-AsyncTask-and-ProgressDialog-Usage – CampbellGolf May 07 '12 at 15:59
  • I also found this to be useful in deciding between TimerTask or ScheduledExecutorService: http://stackoverflow.com/questions/409932/java-timer-vs-executorservice – CampbellGolf May 07 '12 at 16:08
0

Can you please post a bit of your code ? It may be useful to understand where your problem is.

As york has pointed it out, you should probably use TimerTask. It seems that it suit better with what you are trying to do.

If it is the creation of a new instance of the Handler that create the probleme you can try something like this :

private Handler mHandler = null;

@Override
public void onCreate(Bundle _savedInstanceState) {
    super.onCreate(_savedInstanceState);
    setContentView(R.layout.my_layout);

    if (mHandler == null) {
        // TODO create your handler here
    }
}

EDIT : You can test _savedInstanceState == null too.
_savedInstanceState is used to save the state of the activity so turning the phone shouldn't be a problem anymore. However, if you leave the activity and then go back to it, it will create a new handler (except if you instanciate it as a static variable).

Cyril Leroux
  • 2,599
  • 1
  • 26
  • 25
  • Yes, I agree with York, I'm going to visit using a Timer instead. BTW, I did try your suggestion to check if mHandler is null before creating a new instance of the Handler which creates a new instance of Runnable... but I still have the problem of spinning up multiple AsynTask threads, and I can easily crash the app by flipping the phone back and forth: – CampbellGolf May 07 '12 at 15:54
  • FATAL EXCEPTION: main java.lang.NullPointerException at com.echo5.appname.ActivityStationsList.onTaskComplete(ActivityStationsList.java:152) at com.echo5.appname.THREADS.AsyncTaskManager.onComplete(AsyncTaskManager.java:61) at com.echo5.appname.ActivityStationsList$UpdateStationTask.onPostExecute(ActivityStationsList.java:573) at com.echo5.appname.ActivityStationsList$UpdateStationTask.onPostExecute(ActivityStationsList.java:1) at android.os.AsyncTask.finish(AsyncTask.java:417) at android.os.AsyncTask.access$300(AsyncTask.java:127) – CampbellGolf May 07 '12 at 15:55
  • at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3687) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600) at dalvik.system.NativeStart.main(Native Method) – CampbellGolf May 07 '12 at 15:55
  • I edited my post. If you try one or both the test on _savedInstanceState and the handler as a static variable let me know if it worked for you. – Cyril Leroux May 07 '12 at 18:22