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?
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.
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).
Related
I have a pretty odd problem here. In a fragment, I do a process and when the process finishes I show an advert that callsback to the fragment when user clicks/dismisses the ad. The problem is that in some devices when the ad calls back to the handler (that is in the running fragment) the activity containing the fragment has been destroyed, but I need to do some more work through a runnable. So, in this case the runnable throws a NullPointerException int is run method when executed.
I could just check if the activity is still alive and run just the runnable when it is, but in the cases it is not alive I still need to continue to do the part of the job that needs to be done after the ad.
How do you handle this kind of situations? I have been thinking about the problem during some hours without finding a solution to this.
Thanks in advance.
You can use AsyncTask in this case .
AsyncTask processes are not automatically killed by the OS. AsyncTask processes run in the background and is responsible for finishing it's own job in any case. You can cancel your AsycnTask by calling cancel(true) method. This will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object) method is called instead of onPostExecute() after doInBackground() returns.
Hope it helps..
mmm the way this is asked I am not sure what you are asking, perhaps some text connectors might work, I am not sure if this is a quite basic question about state changes or a very complex one.
from what I understood:
wouldn't this be the same problem as when you flip screen? make a Bundle of the data that is restored through activity changes. This way if your activity has been Destroyed you restore it
fragments have a feature that you can use to keep instance alive across a configuration change: retainInstance
setRetainInstance(true) // false by default
Parcelable like Serializable, is an API for saving an object out to a stream of bytes. Objects may elect to implement the Parcelable interface if they are what we will call "stashable" here. Objects are stashed in Java by putting them in a Bundle, or by marking them Serializable so they can be serialized, or by implementing the Parcelable interface. Whichever way you do it, the same idea applies: you should not be using any of these tools unless your object is stashable
---or---
turn that "advert" of yours into an Alert, which wont mess with the Activity.
---or---
run the response on a different thread?
I know you are using it quite well with your Non-UI codes in AsnycTask but I am just wondering if there is any kind of problem while using AsyntTask? I am not having any code which produce the problem. But I am just curious to know Any bad experience if you have with AsnycTask and would like to share it.
Memory Leak :
Even though activity is destroyed, AsyncTask holds the Activity's reference since it has to update UI with the callback methods.
cancelling AsyncTask :
cancelling AsyncTask using cancel() API will not make sure that task will stop immediately.
Data lose :
When screen orientation is done. Activity is destroyed and recreated, hence AsysncTask will hold invalid reference of activity and will trouble in updating UI.
Concurrent AsyncTasks: Open Asynctask.java go to line number 199, it shows you can create only 128 concurrent tasks
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
Rotation: When Activity is restarted, your AsyncTask’s reference to the Activity is no longer valid, so onPostExecute() will have no effect.
Cancelling AsyncTasks: If you AsyncTask.cancel() it does not cancel your AsyncTask. It’s up to you to check whether the AsyncTask has been canceled or not.
Lifecycle: AsyncTask is not linked with Activity or Fragment, so you have to manage the cancellation of AsyncTask.
There are some workarounds to solve above issues for more details have a look at The Hidden Pitfalls of AsyncTask
I just want to share the information that if you are using Asynctask, it will keep on doing its work even of the activity does not exist.
So in case you have asynctask which starts in onCreate() of the activity, and you rotate the device. At each rotation, a new activity is created with a new instance of Asysntask. So many requests will be send over the network for same task.In this way, a lot of memory will be consumed which effects the app performance resulting in crashing it. So to deal with it Loaders(Asynctask Loaders) are used.
For more info check the video:
Loaders
I have an app which makes rest calls and represents the data in a GridView.
The main activity uses two fragements, a UI Fragment and a retained worker fragment. The worker fragment contains an inner AsyncTask that performs the REST calls.
Everything was working great I had no crashes etc, until I tried to do an update on a regular interval.
In order to perform the interval I added a handler. The handler is a member of the worker fragment. Within the worker fragment I have the LoadAPI method which calls the asynctask.
public void loadAPI(){
final String myURL = "http://x.com/"
handler.post(new Runnable() {
public void run(){
new APITask().execute(myURL);
handler.postDelayed(this,10000);
}
});
}
The problem is when there is a config change, my activity is destroyed, and onPostExecute crashes when it references the main activities listener. But I have implemented onAttach in my worker fragment. onCancel seems an ugly option, as multiple asynctasks can get called, and I don't have a named instance. I suppose I could keep a list of asynctasks in the worker fragment and cancel them onDestroy (It's ok to lose the latest update) but I think I am doing something wrong here. What is frustrating is the worker frag and asynctask were working fine until I did a continuous polling and I can't figure out why the introduction of the handler is causing this behavior.
My api tasks will take anywhere from 50 milisecond to 5 seconds.
Retained fragments will not get recreated during config changes like rotations, but they will still get destroyed and recreated when system will kill your app because it is in background for example.
so to be safe you should at least:
Never put your async task inside fragment as inner class, if you want to have it inside your fragment class body, then make it static. Otherwise AsyncTask will keep internal reference to your fragment and will prevent it from being garbage collected, and whats more bad is that in onPostExecute you will access your destroyed fragment.
When creating your asynctask, pass a reference to fragment to it, and store this reference inside WeakReference<>, ex:
private WeakReference<DataFragment> fragmentRef;
then in onPostExecute, before using fragment check if fragmentRef.get() returns non-null.
If you need continuous data updates, then consider using IntentService, or even WakefulIntentService. It will be slightly more difficult to report data updates progress from service to activity - but it can be managed with broadcasts. Also, if you want to do data updates from background then you will have to use service, together with alarms - then WakeFullIntentService (or regular service) is the way to go: https://github.com/commonsguy/cwac-wakeful for further reading.
what would be the best way to continue the current Activity once its internal BroadcastReceiver onReceived has been called.
is it possible to start another thread from within the onReceived() method, and also can I start a AsyncTask inside the onReceived() ?
is it possible to start the same activity again from within the onReceived method?
thanx
Please note that an Activity does not survive a screen orientation change -- it gets re-created along with the View hierarchy. From the MVC viewpoint, an Activity is a Controller. Data that must survive an orientation change must go to the Model.
If you keep a reference to the Activity that has gone from the screen due to orientation change, that's a resource leak, and if the Activity receives the result of some AsyncTask, that's also a waste of CPU time and battery power.
Having said that, Activity defines runOnUiThread (Runnable action) which most likely is not what you really need, but it will at least work.
I mean, after the receiver's method is called, the normal cycle continues (reaction on events etc.), so you just need to react on the event reported to your BroadcastReceiver and reach the closing brace of the function.
If you ask if you can rely on that the same instance of SomeActivity will be shown when a long operation completes, the answer is no. For example, the user starts a long operation, then changes the screen orientation. The first SomeActivity, the one that started the long operation, will not be shown when the long operation completes.
I have two static tables with about 500 records each which provide lookup material for some ListViews in my app. When the app first starts and the tables are created I run a process to populate the tables which takes less than a minute but I've been looking to run the process in background using Async Task with a progress dialog letting the user know what is happening.
My concern is that while the process is running and the data is being added and then the user tilts the phone the process will cancel. What would be the state of my database then? Is Async Task the best solution for this or should I use Threading?
So when you rotate or change the orientation of the phone, the activity is the only thing destroyed. You don't necessarily have to get rid of the async task. In fact it will live on. Just don't let another task come in and work on it ad-hocly.
So if you want to have your activity act as if upon rotating that you can start right back up, where you left off, there is a method called onRetainNonConfigurationInstance(). It's basically the method that stashes objects which can't be parceled like in saveInstanceState()
So the idea being:
public void onCreate(Bundle a) {
...
AsyncTask myTask = getNonConfigurationInstance();
if (myTask == null) {
myTask = new AsyncTask();
myTask.execute();
}
...
}
public Object onRetainNonConfigurationInstance() {
return myTask;
}
This will keep the async task running, and when you get your onCreate called after the rotation you just pick it back up and do what needs to be done.
One thing to be conscious of is the progressView. It will have to be destroyed and reinitialized to the new state. Also the overall dismissing of it and showing it in the first place should be done outside the AsyncTask. But nothing is to say that the AsyncTask can't call some callback that you always set in your onCreate() so that it will notify to tell to update the UI or play a sound of completion, etc.
You could also decide to handle the configuration changes on your own through the use of the android:configChanges in your manifest.
You then implement the onConfigurationChanged method and perform any actions inside.
See the developer doc.
When a configuration change occurs at
runtime, the activity is shut down and
restarted by default, but declaring a
configuration with this attribute will
prevent the activity from being
restarted. Instead, the activity
remains running and its
onConfigurationChanged() method is
called.