Tuesday, 31 December 2013

Android Background Processing with Handlers and AsyncTask and Loaders


Welcome Friends !!

Most of the time it is very difficult to understand how we are going to handle the async task in application development. The heavy task like downloading a image from the internet or other networking task should not be done from the main thread.

 I am going to write an article that describes the usage of Threads, Handlers and AsyncTask in your application. It also covers how to handle the application lifecycle together with threads. It is based on Eclipse 4.2, Java 1.6 and Android 4.2.

1. Android's user interface thread

1.1. Main thread

Android modifies the user interface and handles input events from one single user interface thread. This thread is also called the main thread.
Android collects all events in a queue and processed an instance of the Looper class.
                          Message Queue in Android with Looper

1.2. Why using concurrency?

If the programmer does not use any concurrency constructs, all code of an Android application runs in the main thread and every statement is executed after each other.
If you perform a long lasting operation, for example accessing data from the Internet, the application blocks until the corresponding operation has finished.
To provide a good user experience all potentially slow running operations in an Android application should run asynchronously, e.g. via some way of concurrency constructs of the Java language or the Android framework. This includes all potential slow operations, like network, file and database access and complex calculations.

2. Using Java threads in Android

2.1. Using Java threading in Android

Android supports the usage of the Thread class to perform asynchronous processing.
Android also supplies the java.util.concurrent package to perform something in the background, e.g. using theThreadPools and Executor classes.
If you need to update the user interface from a new Thread, you need to synchronize with the user interface thread.

2.2. Disadvantages of using Java threads in Android

If you use Java threads you have to handle the following requirements
in your own code:
  • Synchronization with the main thread if you post back results to the user interface
  • No default for canceling the thread
  • No default thread pooling
  • No default for handling configuration changes in Android

3. Concurrency constructs in Android

Android provides additional constructs to handle concurrently in comparison with standard Java. You can use theandroid.os.Handler class or the AsyncTasks classes. More sophisticated approach are based on the Loader class, retained Fragments and services.

4. Handler

4.1. Purpose of the Handler class

The Handler class can be used to register to a thread and provides a simple channel to send data to this thread.
Handler object registers itself with the thread in which it is created. For example, if you create a new instance of the Handler class in the onCreate() method of your activity, the resulting Handler object can be used to post data to the main thread.
The data which can be posted via the Handler class can be an instance of the Message or the Runnable class.


4.2. Creating and reusing instances of Handlers

To use a handler you have to subclass it and override the handleMessage() method to process messages.
Your thread can post messages via the sendMessage(Message) method or via the sendEmptyMessage() method to theHandler object.
To process a Runnable you can use the post() method.
To avoid object creation you can also reuse the existing Handler object of your activity.
// Reuse existing handler if you don't 
// have to override the message processing
handler = getWindow().getDecorView().getHandler(); 
The View class allows you to post objects of type Runnable via the post() method.

4.3. Example

The following code demonstrates the usage of a Handler via a View.
Assume your activity uses the following layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:indeterminate="false"
        android:max="10"
        android:padding="4dip" >
    </ProgressBar>
    
   <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" >
      </TextView>
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startProgress"
        android:text="Start Progress" >
    </Button>

</LinearLayout> 
With the following the ProgressBar get updated once the users presses the Button.
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

public class ProgressTestActivity extends Activity {
  private ProgressBar progress;
  private TextView text;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    progress = (ProgressBar) findViewById(R.id.progressBar1);
    text = (TextView) findViewById(R.id.textView1);

  }

  public void startProgress(View view) {
    // do something long
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i <= 10; i++) {
          final int value = i;
           doFakeWork();
          progress.post(new Runnable() {
            @Override
            public void run() {
              text.setText("Updating");
              progress.setProgress(value);
            }
          });
        }
      }
    };
    new Thread(runnable).start();
  }

  // Simulating something timeconsuming
  private void doFakeWork() {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

} 

5. AsyncTask

5.1. Purpose of the AsyncTask class

The AsyncTask class encapsulates the creation of a background process and the synchronization with the main thread. It also supports reporting progress of the running tasks.

5.2. Using the AsyncTask class

To use AsyncTask you must subclass it. AsyncTask uses generics and varargs. The parameters are the following AsyncTask <TypeOfVarArgParams , ProgressValue , ResultValue> .
An AsyncTask is started via the execute() method.
The execute() method calls the doInBackground() and the onPostExecute() method.
TypeOfVarArgParams is passed into the doInBackground() method as input, ProgressValue is used for progress information and ResultValue must be returned from doInBackground() method and is passed to onPostExecute() as a parameter.
The doInBackground() method contains the coding instruction which should be performed in a background thread. This method runs automatically in a separate Thread.
The onPostExecute() method synchronizes itself again with the user interface thread and allows it to be updated. This method is called by the framework once the doInBackground() method finishes.

5.3. Parallel execution of several AsyncTasks

Android executes AsyncTask tasks before Android 1.6 and again as of Android 3.0 in sequence by default.
You can tell Android to run it in parallel with the usage of the executeOnExecutor() method, specifyingAsyncTask.THREAD_POOL_EXECUTOR as first parameter.
The following code snippet demonstrates that.
// ImageLoader extends AsyncTask
ImageLoader imageLoader = new ImageLoader(imageView);

// Execute in parallel
imageLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "http://url.com/image.png"); 

5.4. Disadvantages of using AsyncTasks

The AsyncTask does not handle configuration changes automatically, i.e. if the activity is recreated, the programmer has to handle that in his coding.
A common solution to this is to declare the AsyncTask in a retained headless fragment.

5.5. Example: AsyncTask

The following code demonstrates how to use the AsyncTask class to download the content of a webpage.
Create a new Android project called de.vogella.android.asynctask with an activity called ReadWebpageAsyncTask. Add the android.permission.INTERNET permission to your AndroidManifest.xml file.
Create the following layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/readWebpage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Load Webpage" >
    </Button>

    <TextView
        android:id="@+id/TextView01"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Placeholder" >
    </TextView>

</LinearLayout> 
Change your activity to the following:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import de.vogella.android.asyntask.R;

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

public class ReadWebpageAsyncTask extends Activity {
  private TextView textView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    textView = (TextView) findViewById(R.id.TextView01);
  }

  private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... urls) {
      String response = "";
      for (String url : urls) {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(url);
        try {
          HttpResponse execute = client.execute(httpGet);
          InputStream content = execute.getEntity().getContent();

          BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
          String s = "";
          while ((s = buffer.readLine()) != null) {
            response += s;
          }

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      return response;
    }

    @Override
    protected void onPostExecute(String result) {
      textView.setText(result);
    }
  }

  public void onClick(View view) {
    DownloadWebPageTask task = new DownloadWebPageTask();
    task.execute(new String[] { "http://www.vogella.com" });

  }
} 
If you run your application and press your button then the content of the defined webpage is read in the background. Once this process is done your TextView is updated.

No comments:

Post a Comment