I have an activity that needs to parse an XML from web, then read in images according to the XML info. To speed things along, I created AsyncTask to do the parsing and image fetching. However, I cannot put the images into the activity from within the thread due to "cannot update view objects from outside the view-creating thread..."
So I moved everything back to the main onCreate routine and it slows things down so much that it's not usable (UI not showing until everything is done (I guess when onCreate returns)) .
Any idea on how to tackle this problem (i.e. parsing/fetching using AsyncTask, then placing/updating images dynamically when each becomes available)? or where in the life-cycle should/can I update the UI?
Thanks in advance for all your help.
How are you using the AsyncTask? It's exactly for the purpose you mentioned: AsyncTask.doInBackground() is running in a separate thread and can be used to do the time-consuming task (loading the bitmap), and AsyncTask.onPostExecute() runs on the UI thread and can be used to perform quick operations that have to happen in the UI thread (i.e. replacing the bitmap in your View).
The async task was the right route. Use the async task to do the work in the background then use onpostexecute.
private class DownloadFilesTask extends AsyncTask<URL, Integer, imageview> {
protected imageView doInBackground(URL... urls) {
//parse and load
}
protected void onProgressUpdate(Integer... progress) {
//update progress if you have progress dialog
}
protected void onPostExecute(imageView result) {
//load image into view.
}
}
See Async task
Try this:
runOnUiThread(Runnable action)
//Runs the specified action on the UI thread.
I implemented this to update a ListView after getting data from the web and it works like a charm. If you download the source from this tutorial ( to the bottom ), you can see an implementation of how this works. :-)
Edit: see this rar file: ( Eclipse source code of an upcoming tutorial :-) )
Related
I have a method that takes data in a very large array and displays a very large table on the Display. The table is so large that the screen goes blank for awhile. Can I put the display method inside an Asynctask and display a progress bar until the display method is complete?
No, you can't access Views from background. You can load data into array in a background thread (so it won't block UI during load) and then use this array in an adapter for ListView. I can answer you with more details if you post your code
As mentioned by Chaosit you can't access the UI from a background thread.
consider loading the data in an async task, for loading the table to the UI try to render it row by row. if you are using a listView there are techniques to add/edit cells without rendering the whole table .
i used the same techniques when dealing with large number of PINs that needed to be rendered as the user move the Map .i load some of them and render them 1 by 1 to the UI .
The best way to load a big volume of data is to show a ProgressBar in onPreExecute(), load your data in doInBackground(...) and set the visibility of the progress bar to GONE in onPostExecute(Boolean success).
Another nice way to do it is display a Dialog calling .show() in onPreExecute() and dismiss it in onPostExecute(Boolean success), maybe updating the Dialog message in the onProgressUpdate(String... message).
Just remember that you cannot update or change your UI in doInBackground(...), but you can in onPreExecute(), onPostExecute(Boolean success) and in onProgressUpdate(String... message).
If the data is coming from a the database (or from a remote source) then consider using Loaders/CursorAdapter - to build your ListView. This is a very efficient way of loading data.
If you're reading Array data from a file then try to break down the data - and just select the data that you're displaying on the screen, rather than reading all the data from the file, and try to render item that will not be displayed - Then you can use AsyncTask or a Thread(and Handler) to update your table.
As the others have mentioned, only the main thread can update the UI.
Surely not all the data is shown at once!
Have a look at the Loaders guide. It will allow you to load a large data set, and display only part of it, reducing load times.
Said functionality can be achieved using asynctask as
class TestAsync extends AsyncTask<Void, Integer, String>
{
protected void onPreExecute ()
{
//Start showing progress bar here ...
}
protected String doInBackground(Void...arg0)
{
// Do long running operation here ...
return "You are at PostExecute";
}
protected void onPostExecute(String result)
{
// Start displaying data in your table view and dismiss the progress bar
}
}
Call it like this in your activity:
new TestAsync().execute();
I was wondering how I could force a new ASyncTask in Android to be the next task executed. Imagine I have multiple ASyncTask's to load content from the internet, e.g. in my case I pre-load all objects of DataModelA, DataModelB and DataModelC. All the data is loaded to a central application model. I guess my current queue would look like this (but I do not manage it as a queue, yet):
1. DataModelA-Object1
2. DataModelA-Object2
3. DataModelA-Object3
4. DataModelB-Object1
5. DataModelB-Object2
6. DataModelB-Object3
7. DataModelB-Object4
8. DataModelC-Object1
9. DataModelC-Object2
10. ...
I start my tasks like this:
DataModelALoaderTask task = new DataModelALoaderTask();
task.execute("http://my-rest-api.com/datamodel/a");
// and the same for the other tasks
...
The task class looks like this:
private class DataModelALoaderTask extends AsyncTask<String, Void, List<DataModelA>> {
#Override
protected List<DataModelA> doInBackground(String... params) {
String data = params[0];
return DataModelAContentProvider.loadContentFromUrl(data);
}
#Override
protected void onPostExecute(List<DataModelA> models) {
// finally add the loaded content to the central application data model
...
}
}
What I would like to achieve:
Now within my GUI, I like to click on a button and force to load a single part of a model first, because I need to display it right now. Usually this is a task which is already in the queue, but in my case I could also just start a new task, e.g. to load DataModelC-Object2 first before all others are loaded. The queue could load a large number of objects, so that it makes sense to re-schedule a single task or start a new task which does the job immediately.
Any idea how I can handle that? If you would need to see more code snippets, please let me know.
Thanks in advance.
Best regards,
Michael
UPDATE: somehow my question seems to be related to this post. Is that the only way? Or could I simply start a parallel running task using the THREAD_POOL_EXECUTOR? How would I use that?
I haven't started coding yet. I'm trying to figure out which libraries that is out there and what the API can offer first.
I need to download a lot of resources before starting my app. Basically I'm going to have a "SplashSreen" and a progressbar that shows which file being downloaded and the progressbar for the entire progress of downloading the x-number of resources.
After looking through some discussions and tutorials on the subject, it seems to me that AsyncTask will do the job. But it's only possible to show the current progress for the current "object" being downloaded, and not for the entire queue.
Could anyone help me out here, give me some tutorials or something to dig into?
TBH; I'm not even sure if AsyncTask is what I'm looking for in this scenario. Basically I need something that can download resources, put in a queue, and let me know the progress of the entire queue, and which resource (name of it) being download at a certain time.
Thanks!
Of course an AsyncTask can do the job. Just put all your files in a asyncTask which display the progress.
class InitTask extends AsyncTask<Void, Integer, Boolean> {
private List<String> mNbFiles;
public InitTask(List<String> filesURL)
{
super();
mFilesURL = filesURL;
}
#Override
protected void onPreExecute() {
//show and init your progress bar here...
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
for(int i = 0 ; i < mNbFiles.size() ; ++i){
//download one file ...
publishProgress(i);
}
}
}
I hope this will help you. Comment if you have any question.
AsyncTask isn't what you should be using for long running downloads. AsyncTasks are really about the UI, and it's important to know that the user navigating away or even rotating their screen will kill the download.
You should use a service to perform the download, so put all of your download code into an IntentService and update some kind of persistant storage to indicate progress (a couple variables in shared preferences would do fine (on to indicate current state, the other to indicate progress)).
You can then use your activity to fire up the service, and then monitor the progress of the downloads via the persistant storage or some callback passed via the intent (persistant storage is probably easiest).
AsyncTask is exactly what you are looking for, you can queue depending on the API an amount of tasks, im going to check the Asynctask implementation if there is a getter for the queue size, but if not you can make a list of asyncTask and update when its finished.
If you queue 15 tasks you can set your progress bar to 15 max and on each onPostExecute increment the progressBar :)
I will check the queue later and edit my answer.
Regards.
AsyncTask is very much what you're looking for. That's not really true that you can only update for the current Object. What that means is that you can update for the current AsyncTask.
Below is the onProgressUpdate method.
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.i(logTag, "onProgressUpdate: " + String.valueOf(values[0]));
View view = getView();
if(view != null)
((ProgressBar)view.findViewById(R.id.progressBar)).setProgress(values[0]);
}
You see it takes any number of integers as the arguments. In your publishProgress method, you could call onProgressUpdate(itemProgress, totalProgress) and then update 2 different progress bars accordingly in onProgressUpdate.
You do all the downloading in doInBackground and each time you want to update the progress bar/bars, call publishProgress which passes your progress ints to onProgressUpdate.
In your AsyncTask doInBackground() method put your code that downloads all your objects and post updates of the progress bar using a handler. Take a look at this example:
http://developer.android.com/reference/android/widget/ProgressBar.html
The following code goes inside doInBackground():
while (mProgressStatus < 100) {
mProgressStatus = doWork();
// Update the progress bar
mHandler.post(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
}
});
}
I have a bit of a problem I cannot solve, since it might a bug or something like that. I would like to make a chart with androidplot, and it works really good, but of course it needs some time to draw it (using hundreds of data), so I use a progress dialog to indicate the loading. But what happens is really weird.
I can define the appearance of the activity when it's loading and when it's loaded. When its loading I define a textview in the background setting its text to "loading" and if it is loaded, that textview contains lots of datas, text etc.
onCreate
{
Thread thread = new Thread(new Runnable() {
public void run() {
-------what needs to be appeared after its loaded ----
Textview -> 12,3245,456,78,789
}
----what is on the screen while the progressbar is on---
TextView -> loading..
}
But most of the time after the progress dialog disappears, nothing happens, the textview still says "loading" and rarely it loads the datas and makes the data appear and changes the textview. What I noticed that the more data I would like to appear the rarelier it stays at the loading phase. Of course everytime the loading progessbar appeers then disappears.
Any Suggestion? It is really weird because I use it in a tablayout and other tabs never do this, always make the data appear.
Thanks in advance!
UP: Okay, its always the first tab, whatever it contains, so the first tab is somehow wrong...
The Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI
from a worker thread—you must do all manipulation to your user interface from
the UI thread. Thus, there are simply two rules to Android's single thread model:
1. Do not block the UI thread
2. Do not access the Android UI toolkit from outside the UI thread
read this for more information on how to access UI elements from outside.
edit::
use AsyncTask ::
onCreate
{
new myLoading().execute();
}
class myLoading extends AsyncTask<Void, Void, Void>
{
protected Void doInBackground(Void ... ) {
.......... do all the loading here .........
}
protected void onPostExecute(Void ) {
Textview -> 12,3245,456,78,789
}
}
I figured out some workaround. I dont have any clue about the solution, my only guess is the loading screen somehow overtake the arrival of all the data and so the chart cannot be drawn. or whatever...
I put a thread.sleep(1000) and now it works.
I have a table with about 1k rows that I want to display. This task obviously chokes the UI thread, resulting in a black screen while the onCreate() builds the table.
I've solved this by using AsyncTask which builds the wanted TableLayout in the "doInBackground" function and display it on the "onPostExecute" function.
Question #1:
Is there any better practice that I'm not familiar with?
Question #2:
My (simplified) "doInBackground" function looks like this:
protected Void doInBackground(Void... v) {
tmpTableLayout = populateTable("");
return null;
}
And my (simplified) "onPostExecute" function looks like this:
protected void onPostExecute(Void v) {
TableLayout ct = (TableLayout)findViewById(R.id.RealTable);
ct.removeAllViews();
/* WHATS HERE? */
}
What should I write instead of the "WHATS HERE?" in the last line of code in order to display the content of "tmpTableLayout" in "ct" ?
Thanks in advance!
Are you sure you want to display it all in one go?
One approach would be to dynamically load in more lines as the user scrolls down. So have a scroll listener that checks if the user is approaching the end of the content that is displayed and therefore start an AsyncTask or a thread loading more content.
Example:
Android List Activity with dynamically loaded images form the web in android
I would probably use a ListView and CursorAdapter and let Android manage fetching the data for you. See the accepted answer here.