Book Image

Instant Android Fragmentation Management How-to

By : Gianluca Pacchiella
Book Image

Instant Android Fragmentation Management How-to

By: Gianluca Pacchiella

Overview of this book

There are currently 7 different versions of operating systems for Android. A growing issue is fragmentation. With the number of Android users and the variety of versions available, Android fragmentation is a huge problem. This little book is the solution. Instant Android Fragmentation Management How-to is a step-by-step guide to writing applications that can run on all devices starting from Android 1.6. With simple solutions for complex problems, this book will walk you through the biggest issues facing Android developers today.This book will take you through the newest features in the latest version of Android, and shows you how to utilize them in the older versions using the compatibility library. This practical guide allows you to focus on  creating the best application possible without worrying about compatibility.All the heavy lifting is done for you. Using user interface, adapting your application will work perfectly on any Android operating system. Asynchronous data management will also allow your applications to run smoothly on any device.Everything you need to run your app on any version of Android is right here.
Table of Contents (7 chapters)
Instant Android Fragmentation Management How-to
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface

Loader (Should know)


In this task, we'll show the use of the class called Loader, a class specifically intended to do asynchronous work in the background in order to update the application-related data; before the introduction of the Loader and related classes, the only way to manage data was using Cursor with some specific Activity class's method:

public void startManagingCursor(Cursor)
public Cursor managedQuery(Uri, String, String, String, String)

The problem with this approach is these calls are on the main application thread and can make the application non-responsive and potentially cause the dreaded ANRs!

In the following steps, we will show the code of an application that loads the RSS from Packt's website by an HTTP request to a web server and obviously this can't be instantaneous; here is where the Loader class will be used. All of this is done with the Support Library; in this way, the application will be compatible with the previous Android platforms.

How to do it...

Let's list the steps required for completing the task:

  1. First of all, include the necessary Support Library classes:

    import android.support.v4.app.FragmentActivity;
    import android.support.v4.app.*;
    import android.support.v4.content.*;
  2. Define a class subclassing the FragmentActivity class as usual and define the onCreate() method that creates the GUI for us:

    public class LoaderCompatibilityApplication extends FragmentActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    }
  3. Define a Fragment that will display the desired data. It's important that it implements LoaderManager.LoaderCallbacks:

      static public class RSSFragment extends ListFragment
        implements LoaderManager.LoaderCallbacks<String[]> {
      }
  4. Implement the adapter for its own data in its onActivityCreated(), and more importantly, call Loader by using the LoaderManager class' method named initLoader():

      @Override
      public void onActivityCreated(Bundle savedInstance) {
        super.onActivityCreated(savedInstance);
    
        setListAdapter(
        new ArrayAdapter<String>(
        getActivity(),
        android.R.layout.simple_list_item_1,
        new String[]{}
        )
        );
        /*
        * Differently to what the documentation says,
        * append forceLoad() otherwise the Loader will not be called.
        */
        getLoaderManager().initLoader(0, null, this).forceLoad();
      }
  5. Now, it's time to implement the methods defined in the LoaderManager.LoaderCallbacks interface:

      public RSSLoader onCreateLoader(int id, Bundle args) {
        return new RSSLoader(getActivity());
      }
    
      public void onLoaderReset(Loader<String[]> loader) {
      }
    
      public void onLoadFinished(Loader<String[]> loader, String[] data) {
        setListAdapter(
          new ArrayAdapter<String>(
            getActivity(),
            android.R.layout.simple_list_item_1,
            data
          )
        );
      }
  6. Finally, define the Loader subclass (there are two functions, doGet() and getNews(), that will not be shown here; they simply retrieve the XML and manage to transform it into an array of strings). In particular, implement the loadInBackground() method. The reader must note that here we are extending the AsyncTaskLoader class that is included in the Support Library:

      static public class RSSLoader extends AsyncTaskLoader<String[]> {
        @Override
        public String[] loadInBackground() {
          String xml = "";
          String[] news;
    
          try {
            xml = doGet();
            news = getNews(xml);
          } catch (Exception e) {
            news = new String[] {e.getMessage()};
          }
    
          return news;
        }
      }
  7. Add a simple layout file:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <fragment   android:name="org.ktln2.android.packt.LoaderCompatibilityApplication$RSSFragment"
            android:id="@+id/rss_list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            />
    </LinearLayout>

How it works...

The preceding code snippet simply facilitates the synchronization between the Fragment class instance to which the Loader belongs and the Loader itself. The first time the Fragment queries LoaderManager by its initLoader() method, a new Loader is created using onCreateLoader() (if a Loader with the given ID already exists, simply return the old instance without calling this method).

From now on, Loader follows the state of the Fragment (it will be stopped when the Fragment will be stopped) and will call the onLoadFinished() method when the data is ready. In the preceding example, the list is updated with the array containing news built on loadInBackground().

There's more...

Now let's talk about some other options, or possibly some pieces of general information that are relevant to this task.

Low level

Under the hood, an Android application is not a unique block of instructions executed one after the other, but is composed of multiple pipelines of execution. The main concepts here are the process and thread. When an application is started, the operating system creates a process (technically a Linux process) and each component is associated to this process.

Together with the process, a thread of execution named main is also created. This is a very important thread because it is in charge of dispatching events to the appropriate user interface elements and receiving events from them. This thread is also called UI Thread.

It's important to note that the system does not create a separate thread for each element, but instead uses the same UI thread for all of them. This can be dangerous for the responsiveness of your application, since if you perform an intensive or time expensive operation, this will block the entire UI. All Android developers fight against the ANR (Application Not Responding) message that is presented when the UI is not responsive for more than 5 seconds.

Following Android's documentation, there are only two rules to follow to avoid the ANR:

  • Do not block the UI thread

  • Do not access the Android UI toolkit from outside the UI thread

These two rules can seem simple, but there are some particulars that have to be clear. First of all, let me show you the simplest way to create a new thread, using the class named Thread.

This class implements the Runnable interface defined with a single method called run(); when an instance of a Thread calls its own method start(), it launches in the background the instructions defined in the run() method. Nothing new for everyone with experience in Java programming; this is plain Java, so it is completely available in all API levels.

For example, if we want to create a simple task that sleeps for 5 seconds, without blocking the UI, we can use the following piece of code:

new Thread(new Runnable() {
  public void run() {
    this.sleep(5000);
  }
}).start();

All is clear, but in a general case, we would like to interact with the UI, in order to update a progress bar, to show an error, or to change the appearance of a UI element; using an example from Android's documentation, we are tempted to write a piece of code where we update an ImageView by using a remote PNG:

public void onClick(View v) {

  new Thread(new Runnable() {

    public void run() {

      Bitmap b = loadImageFromNetwork("http://example.com/image.png");

      mImageView.setImageBitmap(b);
    }

  }).start();

}

All seems ok, but when running this code, it results in an infamous exception appearing in the application's log:

Only the original thread that created a view hierarchy can touch its views.

This is because setImageBitmap() is executed in the thread created by us and not in the UI thread, violating the second rule expressed above (this is not allowed since the UI thread is not thread-safe, that is, it is not assured that concurrent access to an element doesn't cause problems).

Before we solve this problem, let me show you the innermost structures introduced by the Android system to manage threads—the Looper and Handler classes.

An instance of the first class is simply used to run a message loop in a thread that will be handled by an instance of the second class. On the other hand, a Handler instance manages message instances between threads, but its context of execution is the thread where it was initially defined.

In order to understand, it's better to write a complex example involving two threads communicating with messages. Suppose we have a generic Activity class, and inside its onCreate() method, we define two threads communicating after every 5 seconds:

  new Thread(new Runnable() {
    @Override
    public void run() {
      Looper.prepare();

      mFirstHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
          android.util.Log.i(TAG, (String)message.obj);
        }
      };

      Looper.loop();
    }
  }).start();

    new Thread(new Runnable() {
    @Override
    public void run() {
      int cycle = 0;
      while (true) {
        try {
          Thread.sleep(5000);

          Message msg = mFirstHandler.obtainMessage();

          msg.obj = "Hi thread we are at " + cycle;
          mFirstHandler.sendMessage(msg);

          cycle++;
        } catch (java.lang.InterruptedException error) {
          android.util.Log.i(TAG, "error: " + error.getMessage());
        }
      }
    }
  }).start();

This is how it appears in Eclipse's thread panel when the code is running:

The more fascinating thing is that there is also a possibility to queue the Runnable classes to be executed in the original thread of the Handler class. Instead of sendMessage(), it is possible to use mFirstHandler.post() with a Runnable class's definition as the argument.

The fundamental point to remember in order to use these classes is to call Looper.prepare() and Looper.loop() in the run() method of the thread and the code related to the Handler class in between—that's all.

The only thread that has yet a Looper defined is the UI Thread that makes some methods available in order to post the Runnable class instance in it.

Now, back to the earlier problem, let me explain how to solve it using the Runnable class; we can post the updating UI code by using a utility method available to any View, such as the View.post(Runnable) method.

Now, we can substitute the line causing the problem with the following code:

            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });

Looper and Handler are important since they are at the core of the system, and more importantly, they have been available since API level 1, making them good resources for writing the Android applications.

Another important class, available since API level 3, is AsyncTask. If you have worked on an application using background threads, it is probable that you have used it since it is intended for this purpose; to facilitate the managing of the threads, and to avoid all the headache and the error-prone code of the Looper and Handler classes.

Its definition is particular. It uses generics; that is, there are some parameters indicated with Params, Progress, and Result that identify the signature of some functions used internally to manage threads.

In particular, AsyncTask has four methods as follows:

  • void onPreExecute(): Its role is to set up the task.

  • protected Result doInBackground(Params...): This is the core of the AsyncTask class and your code has to be written here. Just after onPreExecute() is terminated, a background thread is created for the execution of this function. It's important to remember not to attempt to update the UI from this function. Use the onProgressUpdate() to post updates back to the UI.

  • onProgressUpdate(Progress...): This is used to publish progresses in some way.

  • onPostExecute(Result): This receives the result of the doInBackground() function.

All but the doInBackground() function are executed in the UI thread, so it's important to remember not to perform time-consuming work in them.

If we want to replicate the code that downloads a remote PNG and updates an ImageView with it, we should write something, as shown in the following code snippet:

class PNGUpdate extends AsyncTask<URL, Integer, Long> {
  Bitmap mBitmap;
  ImageView mImageView;

  public PNGUpdate(ImageView iv) {
    mImageView = iv;
  }

  protected Long doInBackground(URL... urls) {
   int count = urls.length;
    for (int i = 0; i < count; i++) {
      mBitmap = loadImageFromNetwork(urls[i]);
    }

    return 0;
  }

  protected void onPostExecute(Long result) {
    mImageView.setImageBitmap(mBitmap);
  }
}

For where we will want to call it, we have to insert a line, such as the following:

new PNGUpdate(myImageView).execute(pngURL)

What you may have noted is that in the initial steps, when we defined our Loader, we subclassed a class named AsyncTaskLoader. It is simply a Loader with an AsyncTask inside; the only difference here is that it doesn't get three parameters in its definition, but only one since it's not supposed by a Loader to return information about the status of an operation (for example, no progress bar is shown).

A final note from the documentation about the serial/parallel execution of threads:

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

General structure of a Loader

The initial instructions about writing a Loader have used the simple AsyncTaskLoader that simplifies a lot for the life of a developer, creating for you the correct subdivision between background threads and UI threads.

This is important, mainly since it avoids wasting your time with little errors, and more importantly, makes the code more modular, avoiding the need of reinventing the wheel. However, now we are to reinvent the wheel in order to understand how to correctly manage the Loader classes with your applications.

The Loader is intended to be used with dynamic data, where it is important to be notified for updates in order to refresh the related element of the UI; in order to notify our loader that the underlying data is changed, we'll implement a class named RSSObservable that controls that the XML (representing the RSS) is different from the previous version. It's important to note that this is a proof of concept and is not intended to be used in the real world. Both the Loader and the Observable classes download the RSS, causing the drain of the battery (and in some case, you will be billed for the bandwidth).

Once you read this code, try to compare it with the original implementation of the AsyncTaskLoader class that you can find in Android's source code in the file frameworks/base/core/java/android/content/AsyncTaskLoader.java. Obviously, we are not going to implement all the things that you can find there.

So let's implement our custom Loader:

  1. Import the required classes:

    import android.content.Context;
    import android.support.v4.content.Loader;
    import android.os.AsyncTask;
    import java.util.Observer;
    import java.util.Observable;
  2. Define our custom loader, extending the Loader class and indicating the implementation of the Observer interface:

    class RSSLowLevelLoader extends Loader<String[]> implements Observer {
        …
    }
  3. Define the internal variables that will reference the Task and Observable instances:

    private Task mTask = null;
    private RSSObservable mTimerObservable = null;
  4. Define the constructor where we initialize all the things needed for the class to work correctly.

    /*
     * Don't retain a reference to the context in the class since this
     * will / can cause a memory leak.
     */
        public RSSLowLevelLoader(Context context) {
            super(context);
    
            mTimerObservable = new RSSObservable();
            mTimerObservable.start(mURL);
            mTimerObservable.addObserver(this);
        }
  5. Define a customized AsyncTask that returns the data of your choice; in its doInBackground() method, simply do the same as the previous example. onPostExecute() warns LoaderManager of the concluded task.

      private class Task extends AsyncTask<Void, Void, String[]> {
      @Override
        protected String[] doInBackground(Void... params) {
          String xml = "";
          String[] news = null;
          try {
            xml = RemoteHelper.doGet("http://www.packtpub.com/rss.xml");
            news = RemoteHelper.getNews(xml);
          } catch (java.lang.Exception e) {
            news = new String[] {e.getMessage()};
          }
    
          return news;
        }
    
        @Override
        protected void onPostExecute(String[] results) {
        // remember: deliverResult() must be called from the UI Thread
          RSSLowLevelLoader.this.deliverResult(results);
        }
      }
  6. Now implement the behavior for the main actions that can be performed on a Loader:

      @Override
      protected void onStartLoading() {
        if (takeContentChanged()) {
          forceLoad();
        }
      }
      @Override
      protected void onStopLoading() {
        if (mTask != null) {
          boolean result = mTask.cancel(false);
          android.util.Log.i(TAG, "onStopLoading() = " + result);
    
          mTask = null;
        }
    
    
      }
      @Override
      protected void onForceLoad() {
        android.util.Log.i(TAG, "onForceLoad()");
        super.onForceLoad();
    
        onStopLoading();
    
        mTask = new Task();
        mTask.execute();
      }
    
      @Override
      protected void onReset() {
        mTimerObservable.stop();
      }
  7. Implement the deliverResult() method:

    @Override
    public void deliverResult(String[] data) {
      if (isReset()) {
        // if there is data to be garbage collected do it now
        return;
      }
    
      super.deliverResult(data);
    }
  8. Write the callback of the Observer interface:

      @Override
      public void update(Observable obs, Object data) {
        /*
        * The default implementation checks to see if the loader
        * is currently started; if so, it simply calls forceLoad().
        */
        onContentChanged();
      }
  9. Write a class representing the Observable interface, where we implement the code that watches and notifies us of data changes:

    public class RSSObservable extends Observable {
      private String mContents = "";
      private String mURL = null;
      private Timer mTimer = null;  
    
      public RSSObservable() {
        mTimer = new Timer();
      }
    
      private class InnerTimer extends TimerTask {
        @Override
        public void run() {
    
          String xml = "";
          try {
            xml = RemoteHelper.doGet(mURL);
          } catch (Exception e) {}
          if (xml != mContents) {
            RSSObservable.this.setChanged();
            RSSObservable.this.notifyObservers(null);
    
            mContents = xml;
          }
        }
      }
    
      public void start(String URL) {
        mURL = URL;
        mTimer.schedule(new InnerTimer(), 10000, 20000);
      }
    
      public void stop() {
         mTimer.cancel();
      }
    }

The more cumbersome part is understanding the underlying flow of the Loader. First of all, there are three states in which it can exist. Those are as follows:

  • STARTED: Loaders execute their loads and notify the Activity class using onLoadFinished().

  • STOPPED: Loaders continue to monitor for changes, but must not deliver results. This state is induced by calling stopLoading() from LoaderManager when the related Activity/Fragment class is being stopped.

  • RESET: Loaders must not monitor changes, deliver results, and so on. The data already collected should be garbage collected.

Each of these states can be reached from the others.

Since all happens asynchronously, it's possible that a notification of data update can reach the Loader instance when the state is different from STARTED; this explains the various checks present in the code.

One thing introduced in the preceding code snippet, not mentioned in the AsyncTaskLoader example, is the Observer/Observable design pattern. The first is defined as an interface and the second as a class, both in the java.util package (and both have been available from API level 1, so do not cause compatibility issues). The observer receives notification of updates by the update() method, whereas the observable registers some observers (by the addObserver() method) to be notified (by the notifyObservers() method) when a change occurs.

Tip

A last note

cancelLoad() is not present in the Loader class version of the Compatibility Library.