Pages

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. 


Wednesday, April 24, 2013

Parcelable versus Serializable

In this post I'm going to talk about the differences between Parcelables and Serializables.

Android Activities are great, aren't they? They allow you to structure your app in a nice and easy way. Switching between two Activities is also really easy. Just write this.startActvitiy(intent) somewhere in your first Activity and you're done.
There's only one thing that's not so easy to do, and that's passing data between these two Activities. If we want to pass data we have to put it in an Intent or a Bundle. This is quite easy for primitive classes such as String or int, but it's a bit harder for other (custom) objects.

If you have some experience in Java, you probably know something about 'serialization'. Since we're using Java in our Android app, we could let our object implement Serializable and we're done, right? Well, not quite. Android provides us with another interface called Parcelable to do this and there's a reason why.

Serializable

First of all let's have a look at our TestObject that implements Serializable.
public class TestObject implements Serializable{



 private static final long serialVersionUID = 643301981519798569L;

 private String mString;

 private int mInt;

 private List<String> mList;

 

 public TestObject() {

  this.mString = "This is a String";

  this.mInt = 777;

  mList = new ArrayList<String>();

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

   mList.add(String.valueOf(i));

  }

 }

    //Getters and Setters...

}

The only thing we had to do to make sure TestObject could be passed with an Intent or Bundle, was implementing it with java.io.Serializable and add a generated serialVersionUID. Now let's have a look at the same class but as a Parcelable.

Parcelable

This is our TestObject, implementing android.os.Parcelable.

public class TestObject implements Parcelable{



 private String mString;

 private int mInt;

 private List<String> mList;



 public TestObject() {

  this.mString = "This is a String";

  this.mInt = 777;

  mList = new ArrayList<String>();

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

   mList.add(String.valueOf(i));

  }

 }

 

 public TestObject(Parcel in) {

  mString = in.readString();

  mInt = in.readInt();

  mList = new ArrayList<String>();

  in.readStringList(mList);

 }



 @Override

 public int describeContents() {

  return 0;

 }



 @Override

 public void writeToParcel(Parcel dest, int flags) {

  dest.writeString(mString);

  dest.writeInt(mInt);

  dest.writeStringList(mList);

  

 }

 

 public static final Parcelable.Creator<TestObject> CREATOR = new Parcelable.Creator<TestObject>() {

        public TestObject createFromParcel(Parcel in) {

            return new TestObject(in); 

        }



        public TestObject[] newArray(int size) {

            return new TestObject[size];

        }

    };

    

    //Getters and Setters...

}
The first thing we notice here is that our class just became a lot bigger. As you can see we have to override two methods, write a Parcelable.Creator and write a new Constructor. Why should you go through all this trouble if you can just simple use Serializable instead? The answer is performance.

Parcelable splits your object up into primitives, sends those primitives and rebuilds the object afterwards. By doing this, it should run a lot faster. Serializable creates a lot of temporary objects and this causes quite some garbage collection, which should always be avoided, especially on mobile devices.

Putting it to the test

Of course, I ran a small test on this. I created an instance of TestObject, added it to an Intent, started another Activity and took our TestObject out of the Intent. I used TraceView for to check how long this process took for Parcelable and Serializable.
Here's our result for using the Serializable interface:


So we can see this whole process took 248ms. 
Now let's have a look at the results of using the Parcelable interface:

As you can see, for only this one object, the process was completed in less than half of the time for our first way. And let's not forget, this is the whole process of switching to another Activity, not just writing and reading the object!

Conclusion

Parcelable works a lot faster than Serializable and is the best way to go. But if we don't need to be faster, and it's possible to just use Serializable (e.g. android.graphics.Bitmap causes problems), just use Serializable and you don't have to bother about anything anymore.

Important notes:

  • Read from the Parcel in the same order that you wrote to it
  • Don't use raw Creators (specify by adding <TestObject> in this example)

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!

Saturday, April 13, 2013

Supporting multiple languages

Hi there,

This post is kind of like a follow-up of my previous post on Supporting screen orientations. It's also an example of how to improve your app by using different resource files. If you create a new Android project with the Android SDK Tools, for example in Eclipse, there will be a res/ folder created in the top level of your project. You will also find res/values/strings.xml. This file contains UI strings of your project.

Values

The Android Developers team advises you to place all your UI strings in this file. One of the advantages of this is that you'll be able to add different languages to the project without having to make any changes in your code. The only thing you need to do is create a new values folder in the res/ directory. The name of this folder should be values followed by a hyphen and the two-lettered language code defined by ISO 639-1. For example: values-fr for French. Just copy the strings.xml file from the default res/values/ folder in there and translate the values.

Once this is done you can just get the right String by adding @string/some_example_string in the layout files or R.string.some_example_string in the Java files. The app will check what the device's language is and check for strings.xml file in the right values folder. If it can't find it, it will just use the strings.xml file in the default values folder.

Raw

This is not only applicable to the values folder. For example, I'm building an app for this thesis I'm writing. It will contain pretty large articles so all this text should be available in Dutch (my mother tongue) and English. I've decided to place these text files in the res/raw/ directory. Just like the values folders, I can create a different raw folder for each language. The files are then available by referring to R.raw.some_example_text_file. In the example below, I got my text by putting getResources().openRawResource(R.raw.introduction) in my Java code. This can also come in handy if you have images that should be different in according to the language of the device (example: a flag).

Here's an example of how these files and folders are structured.
Notice that there are other files (dimens.xml & styles.xml) in the default values folder but not in the other ones. This is because I don't need to have different dimensions and styles for using another language.

Hope you liked this post and feel free to comment below!

Supporting portrait and landscape screen orientations

When you are trying to make the next best app, it's important that you make sure your app looks great in both portrait and landscape orientation. Although the Android system already scales the default layout for both orientations, you should still make sure you optimize your application for the different orientations. Not only does it maximize the user experience of your app. But it can also provide an extra feature to you app. For example: showing a map in landscape view and a list in portrait view.

Now how do we make sure both of the orientations are optimized? Android provides a way to do this as effortless as possible. The only thing you have to do is create a new sub folder in the res folder. Let me just point out the three folders you can use/make to do this:

  • layout-land: The Android system will use these layouts when the device is in landscape view.
  • layout-port: The Android system will use these layouts when the device is in portrait view.
  • layout: This is the default folder for your layouts. If the system could not find the specified folder of the orientation, it will use the layouts inside this folder. So basically you just need to make one of the folders above.
Just make sure the layout files have the same name. That's all you have to do, it's just as simple as that.

Here's a small example of how your layouts should be organized.



Tuesday, April 9, 2013

Optimizing your Java code in Android (part 2)

It took me a bit longer than I had hoped, but I was finally able to write this next post in my series on Android Optimization. Some of the things in this post may seem obvious for people that have some experience in Java, but I have noticed that there are a lot of people who haven't seen that much (or none at all) Java code that get started on Android right away. So I'll mention these things in here as well.

I/O streams

I/O stands for Input and Output streams. We use these streams to read from and write to files on the device or on other devices. The classes for this can be found in the java.io package.

Closing streams

The first thing I want to start is closing streams. I just spent quite some time today at work figuring out why some files could not get deleted. The person who wrote the code forgot to close the InputStream of a file :/

So, whenever you are using an Input or OutputStream, always close it afterwards. The best way to do this is in a finally block. This way it will always be closed when the job is done, even if something went wrong. Here's an example:

FileInputStream fileInputStream = null;
  try
  {
   fileInputStream = new java.io.FileInputStream(someFile);
   fileInputStream.read ();
  }
  catch (java.io.FileNotFoundException e1)
  {
   System.out.println("Exception : File not found");
  }
  catch (java.lang.Exception e)
  {
   e.printStackTrace();
  }
  finally
  {
   fileInputStream.close ();
  }

Remember this. This will save you (and others) a lot of trouble.



Buffering

Most of the streams read and write one byte at a time. Unfortunately, this causes bad performance because it takes a lot of time to read/write when you are dealing with large amounts of data. But have no fear, java.io provides Buffered streams that can override this byte by byte behavior.

These classes are BufferedInputStream and BufferedOutputStream. The BufferedInputStream stores all the bytes that the FileInputStream sends until it reaches a limit. This limit is by default 512 bytes, but you can change this. Once this limit is reached, the BufferedInputStream sends the data.

If you want, you can also create a custom buffer. This can be done really easy by adding a byte[] to the constructor. This is probably one of the fastest ways to send a file. It all depends on the size of your file. Since I haven't got a lot of time, I looked for an example of using the streams online instead of writing one myself. But nevertheless it should explain everything.

This first method uses no Buffered stream, so sends everything byte by byte.
public static void readWrite(String fileFrom, String fileTo) throws IOException{

             InputStream in = null;

             OutputStream out = null;

             try{

                          in = new FileInputStream(fileFrom);

                          out = new FileOutputStream(fileTo);

                          while(true){

                                          int bytedata = in.read();

                                          if(bytedata == -1)

                                          break;

                                          out.write(bytedata);

                          }

             }

             finally{

              if(in != null)

                          in.close();

              if(out !=null)

                          out.close();

             }

}

This second method uses a Buffered stream with the default buffer of 512 bytes
public static void readWriteBuffer(String fileFrom, String fileTo) throws IOException{

             InputStream inBuffer = null;

             OutputStream outBuffer = null;

             try{

                          InputStream in = new FileInputStream(fileFrom);

                          inBuffer = new BufferedInputStream(in);

                          OutputStream out = new FileOutputStream(fileTo);

                          outBuffer = new BufferedOutputStream(out);

                          while(true){

                                          int bytedata = inBuffer.read();

                                          if(bytedata == -1)

                                          break;

                                          out.write(bytedata);

                          }

             }

             finally{

              if(inBuffer != null)

                          inBuffer.close();

              if(outBuffer !=null)

                          outBuffer.close();

             }

}

This last methods uses the method available() to define the size of the byte array. This is not always the best way, but it will do for this example.
         

public static void readWriteArray(String fileFrom, String fileTo) throws IOException{

             InputStream in = null;

             OutputStream out = null;

             try{

                          in = new FileInputStream(fileFrom);

                          out = new FileOutputStream(fileTo);

                          int availableLength = in.available();

                          byte[] totalBytes = new byte[availableLength];

                          int bytedata = in.read(totalBytes);

                          out.write(totalBytes);

                            

             }

             finally{

              if(in != null)

                          in.close();

              if(out !=null)

                          out.close();

             }

}          

}

A possible outcome of these methods (depending on the file):

  • the first method took 660 ms
  • the second method took 270ms
  • the third method took only 1 ms
Very important note: Custom buffering takes lot of memory if your file size is large, you should be careful about the memory capability of your system. If custom buffering takes lot of memory, try to reduce the array size so that the memory usage will not be large.