android background activities for laying out views - android

I need to load 30+ images to a page (with teaser text). I load all the images via an AsyncTask, but I can't modify the current layout because you can only perform modifications to a view on the main thread. What's the best way to load EVERYTHING asynchronously so that my app doesn't have to hang during a "page load" ?

I don't think you can build a view in the background. You're best bet would be to show a progress dialoge so the user understands why the app is busy.

If your class extends activity, you can use
runOnUiThread(new Runnable() {
public void run() {
//Update UI elements in this block
}
});

Why cannot modify layout? for AsyncTask, you have onProgressUpdate that guarantees to run on UI Thread. You just need to call publishProgress in your doInBackground code.
==Update==
private class LoadImageTask<Url, String, String> extends AsynTask{
doInBackground(Url... urls){
for(int i=0; i<urls.length; i++){
File img = DownloadImage(urls[i]); // Your download method..
publishProgress(img.filename); // Suppose it is saved in sd card
// publish progress will call onProgressUpdate immediately, but on different thread
}
}
onProgressUpdate(String filename){
// Update the UI.. this method will run on UI thread
displayImage(filename);
}
}

Related

Android Activity UI Performance (AsyncTask while-loop)

How can I make this more efficient, at the moment it hangs the activity, giving the following information in logcat every time the while-loop completes I assume:
I/Choreographer: Skipped 55 frames! The application may be doing too much work on its
main thread.
Basically on a while-loop it reads a string variable, modifies and splits the string into parts which are then multiplied then divided, these final values are picked up by an interface which changes the custom UI element in the activity.
This seems too heavy on the main UI thread, I was under the impression that handler.post alleviates some of this by adding to the message queue however there are skipped frames.
I've tried to convert this code into an AsyncTask however I don't understand how this code can be converted to work in AsyncTask.
EDIT: AsyncTask custom class replacing old Thread while-loop.
(Old code for reference: http://pastebin.com/Dek6uQTE)
I'm still unsure how this fits in with AsyncTask, I have added the heavy code within the doInBackground() method however readBuf.replace and readBuf.split cannot be resolved. I thought to put the end changed in onProgressUpdate() as opposed to onPostExecute() as this would keep the UI elements updated automatically.
private class PostTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
readBuf = ((MyApplication) getApplication()).getReadBuf();
}
#Override
protected Void doInBackground(Void... readBuf) {
while (readBuf.length > 4) {
readBuf.replace("V", "");
String[] parts = readBuf.split(",");
String part1 = parts[0];
String part2 = parts[1];
speed1 = Float.parseFloat(part1);
speed2 = Float.parseFloat(part2);
finalspeed1 = (speed1 * 102) / div1;
finalspeed2 = (speed2 * 602) / div1;
publishProgress();
}
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
speedometer.onSpeedChanged(speedometer.getCurrentSpeed() - speedcur1);
speedometer.onSpeedChanged(speedometer.getCurrentSpeed() + finalspeed1);
speedometer1.onSpeedChanged(speedometer.getCurrentSpeed() - speedcur2);
speedometer1.onSpeedChanged(speedometer1.getCurrentSpeed() + finalspeed2);
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
You're starting a new thread which is actually just posting a new Runnable to the UI thread (handler is created on the UI thread), so it's not being done in the background. Here are a few tips to help with this:
If you are going to use a background thread to do the "work", then don't post a Runnable which is performing work to the handler, just post the result and update your UI accordingly.
Don't call findViewById() in a loop or any time after creation, if possible. Get a reference to the UI element and stash it off. This method is expensive as it does a search through your view hierarchy for the matching ID.
If you are going to use an AsyncTask just move the "work" part of your Runnable to the doInBackground() method and your UI updates into the onPostExecute() method.
Whether using a custom background Thread or AsyncTask, be sure to shutdown/cancel the work when your Activity leaves the run state, otherwise you will encounter problems as these components are not lifecycle aware.
It's not clear where readBuf is actually pulling data from, so this may also need some work. If this is real-time data coming from some other source, then you may need to have the custom Thread loop with a small yield. If using an AsyncTask, you'll have to create a new one each time as they are one-shot operations and not intended to be used as a long running background thread.
This article on AsyncTask has more details about how it works and pitfalls.

Android ProgressBar not updating as expected?

I am dynamically populating a TableLayout with rows using an AsyncTask and am not getting the expected ProgressBar behavior.
I initially set the max value of the ProgressBar to three times the number of items I'm processing to accommodate three operations. Here's my code:
onPreExecute:
progress.setMax(items.size() * 3); // We need to create the rows and add listeners to them
Async Class
public class TableLoader extends AsyncTask<Object, String, Boolean>{
#Override
protected Boolean doInBackground(Object... params) {
for (int i = 0; i < items.size(); i++) {
// doStuffToCreateRow
rows.add(row);
progress.publishProgress();
}
}
Then I have:
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (!isCancelled()) {
for (int i = 0; i < rows.size(); i++) {
table.addView(rows.get(i));
progress.incrementProgressBy(1);
}
table.requestLayout();
listener.onTaskCompleted();
}
}
Where publishProgress is simply:
protected void onProgressUpdate(String... values) {
progress.incrementProgressBy(1);
}
Finally, in my onPostExecute method, I have a callback to the activity that uses the AsyncTask, which looks like this in the main activity:
public void onTaskCompleted() { // TableRows have been generated
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
for (int i = 0; i < itemTable.getChildCount(); i++) {
TableRow row = (TableRow) itemTable.getChildAt(i);
// Create and add a listener to that row's checkbox
// progress.incrementProgressBy(1);
}
progress.setVisibility(View.GONE);
}
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion (generation of TableRows). Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
Why is my progressBar not updating as expected?
I've looked the issue up but have found people incrementing the bar by < 1 using division or not using the UI thread. To my knowledge, I'm updating using the UI thread and incrementing by 1/number of items * 3 each time.
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion
It means the Async part is working properly, your first third is the one involving doInBackground / ProgressUpdate, that is, heavy stuff is done outside of the UI thread and just signals progress update. The UI thread is sleeping and happily waiting for update requests.
Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
The other two operations take place in the UI thread. Both for in onPostExecuted and onTaskCompleted run on the UI Thread and are tight loops. The UI thread is blocked in your loops. So while you are inside those tight loops no matter how many thousand times you mingle with any UI component: setprogress, settext, etc.... It will not update until the for loop ends, your routine finishes, and the system takes control again of the UI thread.
What you can do is, as it looks like there's a big number of rows, to add them in chunks then update the progress. This will give some air. Take a look at the following prototype
Handler handler=new Handler();
interface AddRowListener {
public void onRowsAdded();
}
private void addRowChunk(final int startRow, final int totalRows, final AddRowListener listener) {
for (int i=startRow; i<startRow+CHUNK_SIZE; i++) {
if (i>=totalRows) {
// we have finished! Just call the listener
listener.onRowsAdded();
return;
}
addView....
}
// After adding some rows, we end here, and schedule a call to this function again
// to continue. The 25ms delay is optional, but it will give some air to Android for sure
handler.postDelayed(new Runnable() {
addRowChunk(startRow+CHUNK_SIZE, totalRows, listener);
}, 25);
}
private void addRows(AddRowListener listener) {
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
// we start the process, it will create row chunks and call the listener when done
addRowChunk (0, itemTable.getChildCount(), listener);
}
So to add the rows you now can do:
addRows (new AddRowListener() {
public void onRowsAdded() {
// the rows have been added.
}
});
By the way, what's your row size? it has to be huge !
PD_ You have to select a CHUNK_SIZE. Just experiment with values until the UI is snappy. I'd say 40-50 is OK, but it's just a wild guess and depends on the phone hardware.
Don't set your progress.setMax() inside doInBackground(), do it inside onPreExecute() instead. You should not modify the UI thread(Main Thread) from a worker/background thread, and there is exactly where doInBackground() runs. onPreExecute(), onProgressUpdate(), and onPostExecute() run on the UI Thread.
Why you are not using a thread to update the progress bar. This approach is perfect to update the progress........but if you want to set manually then first set the max for progress bar just call below method in your onPreExecute and then onPostExecute
progressBar.setProgress(value);
Where Value should be half of the max when you are calling from onPreExecute and then cent percent from onPostExecute......but I recommend you to use a thread to update the correct progress.
Do not use publishProgress in onPostExecute. Just call incrementProgressBy.

How to load one image at a time in the Android UI thread

I currently have a UI that uses an AsyncTask class to make a web service call. This call gets several pictures and stores them in a HashMap. The UI is then notified and the UI is created with these images. However, this sometimes takes as long as 10 - 15 seconds, which is annoying. Is there a way to populate the UI one picture at a time with a background service so that the user starts to see images faster than they currently are?
Thanks.
here is a rough example of how to do this:
public Void doInBackground(String... urls) {
int len = urls.length;
for(int i = 0; i < len; ++i) {
Bitmap b = downloadImage(urls[i]);
publishProgress(new Object[] {b, i});
}
return null;
}
public void onProgressUpdate(Object... values) {
Bitmap b = (Bitmap)values[0];
Integer bitmapIndex = (Integer)values[1];
//replace the image at location "bitmapIndex" in your collection of images with "b"
//update your adapter via notifyDataSetChanged
}
Use the AsyncTask's onProgressUpdate method to publish progress inside doInBackground. This method(onProgressUpdate) runs on the UI thread.
I don't know if you're aware of this, but just in case, an AsyncTask has the onProgressUpdate function which you could use to perform operations on the UI thread, before the AsyncTask ends with calling onPostExecute. From the doInBackground method just call publishProgress with relevant arguments.

use of asynctask inside asynctask

I wanted to use AsyncTask to load images to the ListView.
private class LoadImageTask extends AsyncTask<HashMap<String,Bitmap>,Void,Bitmap>{
#SuppressWarnings("unchecked")
#Override
protected void onPostExecute(Bitmap result) {
if(model.getIconCache().get(cellIcon)!=null){
icon.setImageBitmap(model.getIconCache().get(cellIcon));
}else{
new LoadImageTask().execute(model.getIconCache());
}
}
#Override
protected Bitmap doInBackground(HashMap<String, Bitmap>... params) {
//return model.getIconCache().get(cellIcon);
return null;
}
}
Ok, I know that this not an affective code. However it works well but with a lot of memory allocation. When reading the documentation about AsyncTask it said that Asynctask can be called only from UI thread, how could it let to use inside itself? And of course I want to make my code work inside a single AsyncTask. "model" in the code is an object that is updated at runtime through another thread. So I need to find a way to use a single Asynctask with periodically control the state of an object. How do I do that? Thanks
only do in backGround runs on backGround thread and postExecute and preExecute run on UI thread itself.. For the same reason u can show and dismiss dialogs in it..
if u want to use single Asynctask for multiple purpose u can play around by passing Different constants.. in .execute() method..
I mean something like this.
Integer Constant1 = 1;
int Constant2 = 2;
and while calling,,
new Background.execute(Constan1 or 2)
and in AsyncTask
doInBackground(Object.. arg0)
{
if(arg0.equals())
{
}
else if(arg0.equals())
{
}
}
Take a look at the asynctask documentation: http://developer.android.com/reference/android/os/AsyncTask.html
private class MyTask extends AsyncTask<Void, Integer, Boolean> {
protected Boolean doInBackground(Void...) {
int i = 0;
while(true){
publishProgress(i++);
}
}
protected void onProgressUpdate(Integer... progress) {
myObject.setState(progress[0]);
}
}
You do your background stuff in the doInBackground method (which runs in the background thread).
You control the state of your object in the onProgressUpdate (which runs on the ui thread)
You send messages between the background thread and the ui thread using publishProgress. These messages could contain the state of your object.

problem loading images asynchronously

I have an application that uses images from web a a source. for my application I'm first downloading images in seperate thread and only if image exists on disk I show on the list. Downloader is a seperate thread, but I'm very new using threads and now something else is confusing me. My images are not shown unless I refresh the view, that is my list. Now I needed to show my images so I wrote this in my custom view :
private Runnable LoaderTask=new Runnable(){
public void run() {
if(cellIcon!=null){
if(iconCache.get(cellIcon)!=null){
icon.setImageBitmap(iconCache.get(cellIcon));
mHandler.postDelayed(this, 200);
}
}
}
};
private class ImageThread extends Thread{
#Override
public void run() {
// mHandler.removeCallbacks(LoaderTask);
mHandler.postDelayed(LoaderTask, 100);
}
}
And I trigger my thread like this
ImageThread thread=new ImageThread();
thread.start();
the handler is instantiated in the beginning of the class. But when I do this n othing happen until I refresh my views. I tried (Activity).runOnUIThread inside one of my threads I got stackoverflow error. How do I solve that or are there any other advices.you can give me? Thanks
Use AsyncTask. In onPostExecute() method just call a method from the activity and refresh the UI. The onPostExecute() is called when the thread is done.
http://developer.android.com/resources/articles/painless-threading.html
P.S. To call an activity's method just use Interface.
Have a look at this:
http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

Categories

Resources