Using Android Async Task to Perform Background Operations

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 Handler 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, AsyncTask, to do the same thing. The concept is same, AsyncTask is a smart helper that encapsulates the creation of thread and handler.

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

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

AsyncTask is a generic class. AsyncTask is subclassed in the form AsyncTask<Params, Progress, Result>.

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

To give a better understanding, a sample application that use AsyncTask will be given.

Creating Sample Application

We’ll create a sample application to demonstrate the use of AsyncTask 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 res/layout/activity_main.xml
    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 @string/start_download we used above is defined in strings resources res/values/strings.xml

    <?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 src/DownloadListener.java
    This is a listener that will be used by AsyncTask 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 src/DownloadTask.java
    This file extends AsyncTask and will do the download task and call the listener to display the progress result. The doInBackground 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 src/MainActivity.java
    This is our main activity that will inflate the res/layout/activity_main.xml to build our main user interface. The activity will create and execute DownloadTask object when Start Button is clicked. Our activity also implements the DownloadListener and set itself as a listener to DownloadTask object. The DownloadTask 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 AndroidManifest.xml
    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 uses-permission in our AndroidManifest.xml related to above features. Add the following lines into AndroidManifest.xml:

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

    Below is our final AndroidManifest.xml:

    <?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

Related Posts:

Android
  • samir

    thanks!!!!