ViewHolder and AsyncTask - android

I've got a problem with updating ListView with AsyncTask.
I use ViewHolders to improve performance, but they are causing problems with AsyncTask. As official android website says passing viewholder to asynctask is not a bad idea (http://developer.android.com/training/improving-layouts/smooth-scrolling.html) but in this case, when AsyncTask lasts long enough it updates two items at once with the same value.
I have an impression, that certain viewholder is used again 4-5 items further, and when asynctask does not finish work before passing again the same viewholder bug happens. After a while this viewholder is updating again and have correct value, but it's not satisfying me.
Is my way of thinking right? How to prevent this?

As you know, Views are reused in ListView's.
So you you are starting an AysncTask in every getView(), multiple Tasks might run to load Data in the same View.
Bugs might occur especially while and after scrolling, if the Task take some time to finish.
How to solve this?
Basically you have to make sure, only one(the latest) Task is running for each View.
This can be done, by saving some kind of reference to the last Task in the View.
And if a new Task is started, you have to check for such a reference and cancel the old Task.
A pretty good description can be found here.
The part "Handle Concurrency" should be most interesting for you.

It depends on how your system is set up. If you launch the AsyncTask when the view is requested, yes, your way of thinking is right. Otherwise, I couldn't tell you.
What you could do is add a boolean in your ViewHolder saying "locked". Set that one to true if you launch an AsyncTask, and let the AsyncTask set it to false when it's done. Before launching an AsyncTask, test if the locked boolean is set to false. That way you'll know not to launch another AsyncTask.

Related

Is it bad practice to access a view in doInBackground()?

Is it bad practice to pass a UI element, such as a TextView, to AsyncTask.doInBackground() (through AsyncTask.execute()) and to read fields from the view in the background thread? (I know it's not okay to alter GUI elements from a background thread.)
It is not great, for the simple reason that you do not know if that widget is any good anymore.
Suppose the following happens:
You execute the AsyncTask, but it is stuck behind other tasks and does not run right away
The user rotates the screen, or presses BACK, or otherwise destroys the activity
Your AsyncTask finally starts running and you try accessing this widget
In the best-case scenario, the widget is simply wrong. In the worst-case scenario, whatever you call on it causes some sort of crash, because the hosting activity is destroyed.
AsyncTask itself is fairly obsolete; modern Android development uses other things (LiveData, RxJava, and Kotlin coroutines being the biggest candidates). However, if you wish to use AsyncTask, please ensure that it does not try referencing the activity or its widgets from doInBackground().

Android AsyncTask inside AsyncTask

So, I'm working on a barcode decoder, which once we have the barcode goes to multiples API over the internet to decode what was just scanned.
The thing is that I have to link some XML parsing together, and I don't know if I'm doing it right.
So, once the barcode is scanned, my program calls an ASyncTask which goes over an API to retrieve the product name. Once it has the name, I want it to call another ASyncTask. I know this is possible by instantiating an ASyncTaks in the onPostExecute() of the other but, I think this is wrong, because it's like boxes within boxes.
So isn't it possible/better to instantiate my second ASyncTask inside my main Activity, and make it wait until my first ASyncTask is finished ?
(english isn't my primary language, I hope I made myself clear).
I think it's absolutely legitimate to start the second AsyncTask in the onPostExecute of the first AsyncTask, Mixing both operations is a bad logical idea, As "The Offspring" said - "You've gotta keep 'em separated"
If you don't want it to be directly inside the onPostExecute itself, set a handler to execute it in the activity and call this handler from onPostExecute.
And last thing - If you have a lot of logic - move it to a separate file, don't keep it all at the same file.
In situations like this it might be better to batch long running operations together in one AsyncTask.
Another option is to use the Loaders API, this makes chaining tasks much easier http://developer.android.com/guide/topics/fundamentals/loaders.html
You can go for another approach if you are facing often a situation like this. That is to merge requests and operations inside of runnables/callables and to manage them separately within say a queue for instance.
Here is a nice approach.
http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

Display loading screen using AsyncTask with ListActivity

I've got an app that uses ListActivity to give users a list of actions. When they click one I use an Intent to launch a separate activity.
My problem is that the actions that the app performs take about 20 seconds to finish, and since I don't want the user to receive that nasty ANR dialog, I tried to use AsyncTask to present them with a loading screen in the mean time. I tried using setContentView(R.layout.loading); on onPreExecute(), but it throws a NullPointerException which as far as I have figured out is due to the fact that loading.xml is not "a ListView whose ID is android.R.id.list".
So what can I do now? How can I show that loading screen? Is there a way around this pretty annoying situation? Any help would be greatly appreciated. Thanks!
I am not sure exactly what your use case is; you have a list of items that are populated immediately, and upon selecting one an action is taken? The action that is taken is to launch another Activity which performs background processing?
Or does it take that long to populate the list of actions?
If the former, you can use an AsyncTask for the long-running activity instead of an Intent to launch another Activity: in the callback you get for the click on the item in question, you would create the AsyncTask, and in doInBackground you would perform the long-running activity, with onPostExecute refreshing or manipulating your list as necessary.
Another thing to consider is using a dialog box to show a loading screen, if the loading is required to happen before you launch a new Activity.
If you can further describe your use case, I can help you more.
It's not the loading screen you need to have on the AsyncTask, it's that 20-second Activity initialization. I would look for a way to do all the setup in a background thread in a Service while the user is free to merrily bop around in other Activities. I'd try hard to find a way not to just stall the user for 20 seconds. Maybe take them to the target Activity and show them data cached from their last visit until the new set is ready.
Fire up and display your loading dialogs in your onCreate() of the Activity being called, then call Dialog.dismiss() in your AsyncTask's onPostExecute().

ListFragment with a Loader and an EndlessCursorAdapter - Nightmare

I will start with what I am trying to accomplish.
I have a ListFragment, with LoaderCallbacks associated to retrieve data from a DB. The data is downloaded using an AsyncTask, and inserted into the DB. When the user gets to the bottom of the list, using the CWAC-Endless widget the AsyncTask is kicked off and downloads more data.
I am facing a couple of issues here, and I have tried to sort this out over many a night, and I have decided to come here to ask for help.
The first issue is configChanges. When the user rotates the device, the Activity is destroyed, and then recreates all of the Fragments. I know I can use setRetainInstance to true, but this does not help as the AsyncTask is still running when the Activity gets torn down!
The second issue is to do with the Loader. If data is downloaded, and the AsyncTask completes fine, then the items appears in the List fine. Lets say there are 20 items in the DB. When the user rotates the device, and the Fragment is recreated, the Loader needs to be associated again. When this happens, the data is not loaded into the list straight away, and instead the AsyncTask for the download is kicked off because the CWAC-Endless adapter thinks its at the last item in the list!
Both of these issues have exhausted me. I need a fresh look on this, as im getting no where.
Any suggestions will do, and I can post up source code if needed.
EDIT
Ok here are a few more details to help with some suggestions.
I am downloading data from the internet, which will only return a set number of items at a time. I then have to request more data when I want it (pagination).
I decided to use a database, as the new Loader functionality makes it so simple to make sure the data is loaded efficiently and consistant, without any threading issues.
If it would make sense to ditch the Loader approach, and use a standard Adapter to render the data, I am more than happy to ditch this approach and use that. I just wanted to see if someone could offer an insight into why this solution is so difficult.
Thanks,
Adam
When the user gets to the bottom of the list, using the CWAC-Endless widget the AsyncTask is kicked off and downloads more data.
FWIW, I have not tried EndlessAdapter with this combination of stuff (cursors, loaders, and AsyncTask). In particular, quoting the docs:
Note that this has been tested with ArrayAdapter extensively but may not work with other adapter types
I am not even quite certain what the use case would be for an EndlessAdapter applied to a local database. If you have a crazy long list (e.g., thousands of rows), the answer isn't "load it progressively" but "provide a different UX to avoid the long list". For shorter lists, just load the whole thing and be done with it. EndlessAdapter is for cases where the loading is expensive (e.g., Internet access).
That being said, I will add "play with EndlessAdapter and Loader" to my to-do list.
I know I can use setRetainInstance to true, but this does not help as the AsyncTask is still running when the Activity gets torn down!
So? onPostExecute() will not be invoked until the new activity has gotten through onCreate(). Moreover, in a fragment-based model, your task should be talking to the fragment, and if that fragment is retained via setRetainInstance(true), it's the same fragment instance in both the old and the new activity.
When this happens, the data is not loaded into the list straight away
It should be loaded in fairly quickly, though asynchronously. Moreover, I don't see why this is any different from when the activity is created in the first place.
and instead the AsyncTask for the download is kicked off because the CWAC-Endless adapter thinks its at the last item in the list
You should not be creating the EndlessAdapter until after you have data.

Asynctask API call issue

I'm currently using an AsyncTask to make an API call and populate a list with data.
I have a Sub Menu whose items can call the AsyncTask to populate the data, problem is that if i click quickly i end up with merged results obviously cause the AsyncTask is running at the same time as each other.
What is the best way to handle a situation like this? Sorry if this is a amateur question.
I would use a ProgressDialog to show that content is being updated, and when the update is complete, dismiss() the dialog. While this is happening, you should make sure that you are not accepting touch input on your ListView. (this may happen by default when the ProgressDialog is in front, I am not remembering currently...)
Take a look at this link for an example.
You could configure only a single Asynch task to run at a time.
A boolean variable which is set to true as soon as the asynch task starts and set to false as soon as it finishes.
Next call could wait for this to be set to false. You could also rate limit what is the min time after which only making the API call makes sense.

Categories

Resources