Pages

Showing posts with label asynctask. Show all posts
Showing posts with label asynctask. Show all posts

Sunday, April 28, 2013

The dark side of AsyncTask

Hi everyone,

In this post I'm going to talk about some of the (less known) problems of AsyncTask. I've written my first post on how to use AsyncTask about half a year ago. Now I'm going to tell you what problems they might cause, how you can fix this and what other options you have.

AsyncTask (°2009 - ...)

AsyncTask was added to Android in API level 3 (Android 1.5 Cupcake) and was said to help developers manage their threads. It's actually also available for 1.0 and 1.1, under the name of UserTask. It has the same API as AsyncTask. You should just copy the source code in your application to use it. But according to the Android Dashboards, there are less 0.1% of all Android devices that run on these API levels, so I mostly don't bother to support them.

Right now, AsyncTask is probably the most commonly used technique on Android for background execution. It's really easy to work with and that's what developers love about it. But this class has a couple of downsides and we are not always aware of them.

Lifecycle

There is quite a misunderstanding about our AsyncTask. Developers might think that when the Activity that created the AsyncTask has been destroyed, the AsyncTask will be destroyed as well. This is not the case. The AsyncTask keeps on running, doing his doInBackground() method until it is done. Once this is done it will either go to the onCancelled(Result result) method if cancel(boolean) is invoked or the onPostExecute(Result result) method if the task has not been cancelled.

Suppose our AsyncTask was not cancelled before our Activity was destroyed. This could make our AsyncTask crash, because the view it wants to do something with, does not exist anymore. So we always have to make sure we cancel our task before our Activity is destroyed.  The cancel(boolean) method needs one parameter: a boolean called mayInterruptIfRunning. This should be true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete. If there is a loop in our doInBackground() method, we might check the boolean isCancelled() to stop further execution.

So we have to make sure the AsyncTask is always cancelled properly. 

Does cancel() really work?

Short answer: Sometimes.
If you call cancel(false), it will just keep running until its work is done, but it will prevent onPostExecute() to be called. So basically, we let our app do pointless work. So let's just always call cancel(true) and the problem is fixed, right? Wrong. If mayInterruptIfRunning is true, it will try to finish our task early, but if our method is uninterruptable such as BitmapFactory.decodeStream(), it will still keep doing the work. You could close the stream prematurely and catch the Exception it throws, but this makes cancel() a pointless method.

Memory leaks

Because an AsyncTask has methods that run on the worker thread (doInBackground()) as well as methods that run on the UI (e.g. onPostExecute()), it has took keep a reference to it's Activity as long as it's running. But if the Activity has already been destroyed, it will still keep this reference in memory. This is completely useless because the task has been cancelled anyway.

Losing your results

Another problem is that we lose our results of the AsyncTask if our Activity has been recreated. For example when an orientation change occurs. The Activity will be destroyed and recreated, but our AsyncTask will now have an invalid reference to its Activity, so onPostExecute() will have no effect. There is a solution for this. You can hold onto a reference to AsyncTask that lasts between configuration changes (for example using a global holder in the Application object). Activity.onRetainNonConfigurationInstance() and Fragment.setRetainedInstance(true) may also help you in this case.

Serial or parallel?

There is a lot of confusion about AsyncTasks running serial or parallel. This is normal because it has been changed a couple of times. You could probably be wondering what I mean with 'running serial or parallel'. Suppose you have these two lines of code somewhere in a method:

new AsyncTask1().execute();
new AsyncTask2().execute();

Will these two tasks run at the same time or will AsyncTask2 start when AsyncTask1 is finished?
Short answer: It depends on the API level.

Before API 1.6 (Donut):

In the first version of AsyncTask, the tasks were executed serially. This means a task won't start before a previous task is finished. This caused quite some performance problems. One task had to wait on another one to finish. This could not be a good approach.

API 1.6 to API 2.3 (Gingerbread):

The Android developers team decided to change this so that AsyncTasks could run parallel on a separate worker thread. There was one problem. Many developers relied on the sequential behaviour and suddenly they were having a lot of concurrency issues.

API 3.0 (Honeycomb) until now

"Hmmm, developers don't seem to get it? Let's just switch it back." The AsyncTasks where executed serially again. Of course the Android team provided the possibility to let them run parallel. This is done by the method executeOnExecutor(Executor). Check out the API documentation for more information on this.

If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel:

 
 public static void execute(AsyncTask as) {
  if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) {
   as.execute();
  } else {
   as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
 }
(This code does not work for API lvl 1 to 3)

Do we need AsyncTasks?

Not really. It is an easy way to implement background features in our app without having to write a lot of code, but as you can see, if we want it to work properly we have to keep a lot of things in mind. Another way of executing stuff in background is by using Loaders. They were introduced in Android 3.0 (Honeycomb) and are also available in the support library. Check out the developer guide for more information. 


Sunday, April 21, 2013

Image Caching with LruCache

Caching images

When we are building an application, whether it's a web app or a mobile app or whatsoever, we want to make sure it runs as fast as possible. One of the most important things we have to keep in mind is caching. If we are using certain resources in our application, we should know that it isn't always that easy to retrieve them. So we should always avoid to retrieve the same resources multiple times. This can be done by caching them. In this tutorial I'm going to give an example of how to cache images in your Android app.

Images can easily get quite large, so retrieving them can take up quite some time. Since mobile devices have limited resources (such as battery life, data transfers, etc), it's twice as important to make sure we cache images the right way.

SoftReferences?

Java already provides an easy way to do this. It's called SoftReferences. You can read all about it here. The only problem is that there is an issue in Android that causes the garbage collector not to respect these SoftReferences. You can read all about this issue here. The Android System provides another way of caching your objects, called LruCache.

LruCache

LRU stands for Least Recently Used. This already tells us a lot about how this cache would work. LruCaches have a fixed cache size and work like a queue, you might say.  If a new object is added to the cache, it's placed in the beginning of the queue. If the app asks to get an object from the cache, it's also placed in the beginning of this queue. This way the least recently used objects are left at the beginning of the queue.



If the cache doesn't have enough memory left, it deletes the least recently used objects until it has enough free space to store the new object(s).


API levels

LruCache was only added to API level 12 but don't worry, they have included this in the support library v4.

Code it!

Okay, it's nice to know how it works but now we still have to implement it in our code. For this tutorial I've made a small app that puts all the thumbnails of the images on the device in a large list. If the user scolls down, the images are loaded. This is actually really simple to implement. First let's have a look on how we could code the LruCache.


public class ThumbnailCache extends LruCache<Long, Bitmap>{



  public ThumbnailCache(int maxSizeInBytes) {

   super(maxSizeInBytes);

  }



  @Override

  protected int sizeOf(Long key, Bitmap value) {

   return value.getByteCount();

   

   //before API 11

   //return value.getRowBytes() * value.getHeight();

  }

  

  

 }

We have to add a value in the constructor of this LruCache. This is the maximum size of the cache in bytes. It can be hard to know how much this should be. You can get some help of the ActivityManager to do this.
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
If we run this code, we'll get the memory limit of our application. This depends on what device you are using (on a Nexus S and Galaxy SII it's 48MB). Have a look here for more information.
It's obvious that we don't want our image cache to take up all of the memory of my application. So we only take a part of this. For this example let's say we use a sixth.
mCache = new ThumbnailCache(memoryClassBytes / 6);
Now place the image in our LruCache when it is retrieved using this line of code.
mCache.put(photoId, bitmap);

And check if it is in cache before retrieving it like this:
Bitmap cachedResult = mCache.get(photoId);

             if (cachedResult != null) {

                 imageView.setImageBitmap(cachedResult);

             }else{
              //retrieves the thumbnail and puts it in an imageview
              new ThumbnailTask(imageView).execute(photoId);

That's all you need to do. Give it a try!

Monday, March 18, 2013

Optimizing your Java code in Android (Part 1)

Hi guys,
This post is a follow-up from my last post on using Traceview for better performance. I explained how to use Traceview to find out which part of the code is slowing down your app. In the example I've written a small method in two ways, a bad way and a better way. Using Traceview we could monitor the usage of the CPU and compare the two parts of code.

I've explained some principles of optimizing your Java code in there. That is:

  • Don't initialize objects in a loop
  • Avoid String concatenation in a loop
  • Use StringBuilder to build a large String.
In this post I'll take some other of these principles for good Java-coding, put them in a small app and compare 'the good way' with 'the bad way' using Traceview.

Use System.arraycopy

In this first example, I've compared two ways of copying an array. First by using a loop, then by using System.arraycopy(). This is the code I've used in the first way:

int[] array = new int[100000];

int l = array.length;

for(int i=0;i<l;i++){

 array[i] = i;

}

  

Debug.startMethodTracing("bonappetit");

int length = array.length;

int[] copy = new int [length];

for(int i=0;i<length;i++)

{

 copy [i] = array[i];

}

Debug.stopMethodTracing();

And this is the code that does the same thing.

Debug.startMethodTracing("bonappetit");

int length = array.length;

int[] copy = new int [length];

System.arraycopy(array, 0, copy, 0, length);

Debug.stopMethodTracing();

I've monitored the both processes with Traceview and saw that the second method ran 3 times as fast.

(To be continued very soon)

Monday, March 11, 2013

Using Traceview for better performance

As some of you might already know, I'm starting a series of posts on optimizing Android apps for my thesis. In this post here, I'm going to give quick preview of how to use Traceview and why it's a great tool to use.

TraceWhat?

What is Traceview and why should I use it? These are the two questions we're going to start with. According to the Android Developers website, Traceview is a graphical viewer for execution logs that you create by using the Debug class to log tracing information in your code. Traceview can help you debug your application and profile its performance.
Basically what it does is it shows you what part of your code is using the CPU a lot. So that already covers the 'what'-part, now let's continue to the why part.

Why should we be interested in knowing how much CPU-time my awesome method needs to run? It's all about performance. Performance is one of the most important topics to focus on, especially in mobile development.We all want our app to be the fastest in the whole Play Store, don't we? By using Traceview properly, we can get a nice graph, showing us what part of our code is slowing down the app the most. Once we know this, we know where to start looking for better performance and get our app running twice as fast. I'll show you in an example below.

Example

For this small example, I'm going to write a simple app that does nothing. Well, nothing interesting that is. This is the code I'm going to run in the doInBackground() method of an AsyncTask.


String result = "";

for(int i=0; i<1000; i++){

 String number = String.valueOf(i);

 result += number;

}

Basically, it just makes on really long String (012345678910111213...).

Now how are we going to make sure that Traceview checks this part of the code? Really simple. All we have to do is add the next two lines somewhere in the beginning and at the end of our code:

Debug.startMethodTracing("bonappetit");

//...

//OUR CODE COMES HERE

//...

Debug.stopMethodTracing();

This will create a file called bonappetit.trace on the SD card of the device. Everything that happens between those two lines of code will be recorded in that file. I've started tracing ath the beginning and the end of the doInBackground() method.
IMPORTANT NOTE: Don't forget to add the permission in the manifest for writing to the SD card!
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

If we open the DDMS perspective in Eclipse, we can have a look at the files on our plugged-in device (or emulator). Select the device on the left side, then go to 'File Explorer' and look into the sdcard-folder for the file. (Mine was under /storage/sdcard0/). Once selected we can click 'Pull a file from the device'. Save the file somewhere on your computer.
Next use your command prompt and go to the folder of your android-sdks. Continue into the tools-folder and type the command 'traceview' followed by the file we just pulled from the device.



If we have a look at the results, we notice that almost 27% of the time was spend on StringBuilder <init>.
In the upper right corner we can see that the whole process took about 800ms, so that's more than 200ms.
If we have a look at our code we can see that we are actually creating a new String object every time in the for-loop. This is why the app spends so much time on StringBuilder <init>.

Now how can we make this better? As a good developer, you should know that using new String objects for creating one long string, isn't a good idea. Java has a class called StringBuilder. Let's try to change our code a bit like this and run the Traceview tool again:

StringBuilder sb = new StringBuilder(4000);

for(int i=0; i<1000; i++){

 sb.append(String.valueOf(i));

}

String result = sb.toString();


If we look in the upper right corner again this time, we can see that the whole process now only took 332ms.
This is more than twice as fast. It is possible to speed this process up even more, but always ask yourself "Does it have to be faster?". Don't waste time on optimizing things that don't need further optimization!

This was an example of Java-optimization, but because Android = Java, I'll make a post on this sort of code optimization as well. Feel free to leave comments below.

Try this out, it could be a great tool for you to use!



source: www.parleys.com

Wednesday, December 19, 2012

Using AsyncTask

A very usefull class in Android is the AsyncTask.

It's an easy way to make your application do something in background and bringing the results of this action to the UI. In this post, I'm going to show you how you can use AsyncTask in your application.
I made a simple App that generates a random number between 0 and 10 every second. After 7 times, it returns the sum of these generated values. In the meanwhile, each time a number is generated, the user will be informed about it.

First of all

Before we start the AsyncTask to do its job, we have to give it some parameters to work with.
While working, we want to see the progress of the AsyncTask.
And of course, eventually, we want to have a result.

If we want to create an AsyncTask, we need to specify these 3 things. Our AsyncTask class will extend AsyncTask<Params, Progress, Result>

In this case, we will make a RandomCountingTask.
The parameters we want to give to the task are the number of times it should generate a number and how large this generated number may be. These are both Integers, in this case 10 the maximum value of a number and 7 for the amount of numbers we want.

The Progress in this case is the generated number, so this is also an Integer.

The Result will also be an Integer, the sum of all generated numbers.

So our task has to look like this:

class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{ }

If we would write this in Eclipse, we would have to add unimplemented methods. This is the method doInBackground(Integer... params){}


AsyncTask 'Lifecycle'

Since the AsyncTask is created to do stuff in background and bring results and progress to the UI, it has methods on the Main Thread (UI) and methods on a single Worker Thread (Background).
This is how it goes:



Code

Now without further ado, here's the code of this simple example:


package com.bonappetit.demos.asynctask;

import java.util.Random;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class AsyncTaskDemo extends Activity implements OnClickListener {

 private TextView txtMain;
 private Button btnStartStop;
 private AsyncTask<Integer, Integer, Integer> countingTask;
 private boolean isCounting;



 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_async_task_demo);
  setUpViews(); 

 }

 private void setUpViews() {
  txtMain = (TextView)findViewById(R.id.txtAsyncTaskDemoMainText);
  btnStartStop = (Button)findViewById(R.id.btnAsyncTaskDemoStartStop);
  btnStartStop.setOnClickListener(this);
  btnStartStop.setText("Start"); 

 }



 @Override
 public void onClick(View v) {
  if(v == btnStartStop){
   if(!isCounting){
    txtMain.setText("");//clear the text
    countingTask = new RandomCountingTask(); // create a new task
    //a task can be executed only once;
    Integer[] values = new Integer[2];
    values[0] = 10; //number between 0 and 10
    values[1] = 7; //generate 7 times
    countingTask.execute(values);
   }else{
    countingTask.cancel(true);
   }
  }  

 }

 public boolean isCounting() {
  return isCounting;
 }

 public void setCounting(boolean isCounting) {
  this.isCounting = isCounting;
 }


 class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{


  @Override
  protected void onPreExecute() {
   super.onPreExecute();
   Toast.makeText(AsyncTaskDemo.this, "Started counting", Toast.LENGTH_SHORT).show();
   setCounting(true);
   btnStartStop.setText("Stop");
  }

  @Override
  protected Integer doInBackground(Integer... params) {
   int maxValue = params[0];
   int maxTimes = params[1];
   int sum = 0;
   int randomNumber;
   Random r = new Random();
   for(int i = 0; i<maxTimes; i++){
    randomNumber = r.nextInt(maxValue);
    sum += randomNumber;
    publishProgress(randomNumber);   

    try {
     Thread.sleep(1000); //Sleep for 1 second
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   return sum;
  }  

  @Override
  protected void onProgressUpdate(Integer... values) {
   super.onProgressUpdate(values);
   txtMain.append("Added " + values[0] + " to the sum\n");
  }



  @Override
  protected void onPostExecute(Integer result) {
   super.onPostExecute(result);
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Stopped counting", Toast.LENGTH_SHORT).show();
   txtMain.append("Result is " + result);
  }  

  @Override
  protected void onCancelled() {
   super.onCancelled();
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Counting was cancelled", Toast.LENGTH_SHORT).show();  
  }
 }

}


Important notes

  • There is a limit of AsyncTasks to use in your application (another post on this will follow)
  • An instance of an AsyncTask can only be executed once.
  • Something like Integer... can be one or more integers