Posts Tagged 'async task'

Using Android Async Task to Perform Background Operations

Posted by:

Async Task is an android helper class that will allows you to run a background processes. If you are a Java programmer, may be you are familiar with thread. If you run a long task in the main thread, the current user interface will be blocked untill the task has finished. This is not a good practice. To avoid the user interface is being blocked, the long task must be perform in another thread.

Async Task Overview

In android, the only thread that can modify the user interface is called UI thread. This is means you can’t modify user interface from another thread. This is a problem, because in another side, we need to run a long task in separate thread and also we need to update the user interface to display the task progress. Actually, android provides a [cci]Handler[/cci] class that allows you to update the user interface from another thread. But we’ll discuss it in the next tutorial.

Rather than create new thread and handler, android has provides a helper class, [cci]AsyncTask[/cci], to do the same thing. The concept is same, [cci]AsyncTask[/cci] is a smart helper that encapsulates the creation of thread and handler.

[cci]AsyncTask[/cci] must be subclassed to be used. There are three methods that need to be overridden, [cci]doInbackground[/cci], [cci]onProgressUpdate[/cci] and [cci]onPostExecute[/cci]. The first method is mandatory to be overriden, the rest are optional.

[cci]doInbackground[/cci] is a method where the execution codes is placed. [cci]doProgressUpdate[/cci] and [cci]onPostExecute[/cci] run in UI thread, so user interface can be modified in both of methods. Put any codes for displaying the task progress on [cci]doProgressUpdate[/cci]. The [cci]doPostExecute[/cci] method will be called once the task has finished.

[cci]AsyncTask[/cci] is a generic class. [cci]AsyncTask[/cci] is subclassed in the form [cci]AsyncTask[/cci].

  1. [cci]Params[/cci], the type of the parameters sent to the task upon execution.
  2. [cci]Progress[/cci], the type of the progress units published during the background computation.
  3. [cci]Result[/cci], the type of the result of the background computation.

To give a better understanding, a sample application that use [cci]AsyncTask[/cci] will be given.

Creating Sample Application

We’ll create a sample application to demonstrate the use of [cci]AsyncTask[/cci] class to perform background processes. Our goal is to create a simple application which download a file from server and display the download progress in the progress bar.

Our application have very simple user interface that only contains a progress bar and a button.

AsyncTask Simple Application

AsyncTask Simple Application

If user click Start Button above, the application will start download a file from server and display the progress in progress bar. After the download process has completed, the file is saved into android root folder (/sdcard).

In this demonstration, I will use a file located on my server, http://semurjengkol.com/dl/files/CustomArrayAdapter.rar(445KB), this is an eclipse project from previous tutorial. You can also use this file or change the url as you want.

Now, open your Eclipse IDE and create new Android Application Project, then follow step by step below:

  1. Create new layout file [cci]res/layout/activity_main.xml[/cci] This file defines the application main layout which contain a progress bar and a button.

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="5dp"
        android:paddingRight="5dp" >
    
        <ProgressBar
            android:id="@+id/progress_bar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:indeterminate="false"
            android:max="100"
            android:progress="0" />
    
        <Button
            android:id="@+id/start_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/start_download" />
    
    </LinearLayout>
    

    The resource [cci]@string/start_download[/cci] we used above is defined in strings resources [cci]res/values/strings.xml[/cci]

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">AsyncTaskSample</string>
        <string name="action_settings">Settings</string>
        <string name="hello_world">Hello world!</string>
        <string name="start_download">Start Download</string>
    </resources>
    
  2. Create new java class [cci]src/DownloadListener.java[/cci] This is a listener that will be used by [cci]AsyncTask[/cci] to communicate with activity to change the user interface.

    package com.sj.asynctask;
    
    import java.io.File;
    
    public interface DownloadListener {
        public void onDownloadComplete(File filename);
        public void onProgressUpdate(int progress);
        public void onDownloadFailure(final String msg);
    }
    
  3. Create new java class [cci]src/DownloadTask.java[/cci] This file extends [cci]AsyncTask[/cci] and will do the download task and call the listener to display the progress result. The [cci]doInBackground[/cci] method retrieves two string parameters:

    1. URL of the file to be downloaded
    2. The destination storage location to save the file
    package com.sj.asynctask;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLConnection;
    
    import android.os.AsyncTask;
    import android.os.Environment;
    
    public class DownloadTask extends AsyncTask<String, Integer, Boolean> {
        /** This is our listener*/
        private final DownloadListener listener;
        /** Hold message if download failure*/
        private String msg;
        /** Save Destination */
        private File saveTo; 
        
        public DownloadTask(DownloadListener listener) {
            this.listener = listener;
        }
        
        @Override
        protected Boolean doInBackground(String... params) {
            /** Params should be array of string with length 2. 
             * 1. url
             * 2. filename destination*/
            if(params == null || params.length < 2) {
                msg = "Incomplete parameters";
                return false;
            }
            
            String sUrl = params[0];
            String filename = params[1]; 
    
            /** get root directory: /sdcard */
            File rootDir = Environment.getExternalStorageDirectory(); 
            /** create destination file*/
            saveTo = new File(rootDir, filename);
        
            try {
                /** define url*/
                URL url = new URL(sUrl);
                /** open the connection*/
                URLConnection conn = url.openConnection();
                conn.connect();
                
                /** get the size of file that will be downloaded. It will be used to measure the progress*/
                int fileLength = conn.getContentLength(); 
                
                /** create input and outpout stream*/
                InputStream is = new BufferedInputStream(url.openStream());
                OutputStream os = new FileOutputStream(saveTo);
                
                /** create buffer*/
                byte buffer[] = new byte[512];
                /** hold total of downloaded bytes*/
                long totalDownloaded = 0;
                int count;
                
                while ((count = is.read(buffer)) != -1) {
                    totalDownloaded += count;
                    
                    /**cause call to onProgressUpdate, which is run on UI thread*/
                    publishProgress((int) (totalDownloaded * 100 / fileLength));                 
                    os.write(buffer, 0, count);
                }
                
                /** closing stream*/
                os.flush();
                os.close();
                is.close();
                return true;
            } 
            catch (MalformedURLException e) {
                msg = "Invalid URL";
            }
            catch (IOException e) {
                msg = "No internet connection";
            }
            
            return false;
        }
        
        @Override
        protected void onProgressUpdate(Integer... values) {
            if(listener != null) listener.onProgressUpdate(values[0]);
        }
        
        @Override
        protected void onPostExecute(Boolean result) {
            if(!result) {
                if(listener != null) listener.onDownloadFailure(msg);
                return;
            } 
            
            if(listener != null) listener.onDownloadComplete(saveTo);
        }
    }
    

  4. Create main activity [cci]src/MainActivity.java[/cci] This is our main activity that will inflate the [cci]res/layout/activity_main.xml[/cci] to build our main user interface. The activity will create and execute [cci]DownloadTask[/cci] object when Start Button is clicked. Our activity also implements the [cci]DownloadListener[/cci] and set itself as a listener to [cci]DownloadTask[/cci] object. The [cci]DownloadTask[/cci] object will call the listener method implementation in the activity when the progress updated, download failure and when download completed.

    package com.sj.asynctask;
    
    import java.io.File;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ProgressBar;
    import android.widget.Toast;
    
    public class MainActivity extends Activity implements OnClickListener, DownloadListener{
        private ProgressBar progressBar;
        private Button startButton;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
        }
        
        private void initView() {
            progressBar = (ProgressBar) findViewById(R.id.progress_bar);
            startButton = (Button)findViewById(R.id.start_button);
            
            if(startButton != null) startButton.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.start_button:
                /** file url to be downloaded*/
                String url = "http://semurjengkol.com/dl/files/CustomArrayAdapter.rar";
                /** destination filename*/
                String filename = "CustomArrayAdapter.rar";
                
                if(startButton != null) startButton.setEnabled(false);
                
                DownloadTask task = new DownloadTask(this);
                task.execute(url, filename);
                break;
            default:
                break;
            }        
        }
    
        @Override
        public void onDownloadComplete(File filename) {
            if(filename != null) {
                Toast.makeText(this, "Download complete. File is saved to " + filename.getPath(), 
                        Toast.LENGTH_LONG).show();
            }
            
            if(startButton != null) startButton.setEnabled(true);
        }
    
        @Override
        public void onProgressUpdate(int progress) {
            if(progressBar != null) progressBar.setProgress(progress);
        }
    
        @Override
        public void onDownloadFailure(String msg) {
            Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
            if(progressBar != null) progressBar.setProgress(0);
            if(startButton != null) startButton.setEnabled(true);
        }
    }
    
  5. Adding permission to [cci]AndroidManifest.xml[/cci] This simple application has two main features:

    • Connecting to internet to download a file
    • Save the file into sdcard storage

    In order to our application runs well, we must add [cci]uses-permission[/cci] in our [cci]AndroidManifest.xml[/cci] related to above features. Add the following lines into [cci]AndroidManifest.xml[/cci]:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />    
    

    Below is our final [cci]AndroidManifest.xml[/cci]:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.sj.asynctask"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="17" />
    
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INTERNET" />
        
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.sj.asynctask.MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    </manifest>
    

Now run the project and click the start button, it will download the file and display the progress on the progress bar

Async Task Download Progress

Async Task Download Progress

0