Android AsyncTask - Download Image/Bitmap

There are plenty of image downloader libraries.Picasso and Glide are so far my best two.However,the truth is they are third party libraries and have to be added as a dependency in android studio or jar in Eclipse.

Sometimes instead of using third party dependencies you just need to do something using the built in classes.

So in this example we see how to download an image/bitmap in the background thread using asynctask.

AsyncTask is an abstract class that makes threading while updating the UI easier.

What we make?

In this tutorial we will make a simple app to download an image from a server in the background thread when a button is clicked. When the user clicks the download button, the app starts downloading the image while a progressDialog is displayed. We display the progressDialog as it's allows our users track the progress of the download from our app.

Tools Used

In this guide we used to Eclipse to create our simple app while we used BlueStacks Emulator to test the app. Bluestacks Emulator is a free emulator however you are free to test with any emulator like say Genymotion etc.

You can also use an android mobile device like Samsung,HTC,Infinix etc whatever device you are using as long as it runs android. However, you have to provide a URL(Uniform Resource Identifier) that is accessible over the web. In this example I created a simple Wordpress website and downloaded the image from it. But you can also provide an internet URL.

NB/= If you are using the emulator and you are downloaing the image from localhost then use the IP address: 10.0.2.2 instead of 127.0.0.1 or localhost.

1. Our MainActivity Class

Here are the responsibilities for this class:

No. Role
1. It's our launcher activity. This class will derive from android.app.Activity.
2. Define the URL we will connect to and download the image.
3. Define an inner class that derives from AsyncTask so that we are able to download our image without freezing the UI.
4. Render the an ImageView, a button and a progressbar as our User Interface controls. The button will be clicked to initiate the download. The progressbar will display the progress of the download. The ImageView will render the downloaded image.

Let's go.

First specify the package name:

package com.tutorials.asyncimagedownload;

Then add these imports:

    import java.io.InputStream;
    import java.net.URL;
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ImageView;

Create a class and make it extend android.app.Activity:

public class MainActivity extends Activity {}

Add the following as instance fields. Please give a url pointing us to the image:

      private String url="http://10.0.2.2/galacticnews/spacecraft.jpg";
      ImageView img;
      Button downloadBtn;
      ProgressDialog pd;

If your image is hosted locally then specify the root ip address as I have, that is if you'll be testing in an emulator.

Let's now create an inner class inside our MainActivity. First here are the roles of this inner Downloader class:

No. Role
1. It's responsible for downloading our image in the background thread and rendering it in an imageview.
2. It shows a progress dialog while download in progress and dismisses it when it's over.
private class Downloader {}

Make the class derive from AsyncTask, an abstract class.:

private class Downloader extends AsyncTask<String,Void,Bitmap>{}

AsyncTask is an abstract class, hence you'll have to override at least the doInBackground() method. But for us firs we override the onPreExecute().This method will get called before our doInBackground()

            @Override
            protected void onPreExecute() {
                // TODO Auto-generated method stub

                super.onPreExecute();

We'll set up our progressbar and show it here inside the onPreExecute():


                //CREATE PD,SET PROPERTIES
                pd=new ProgressDialog(MainActivity.this);
                pd.setTitle("Image Downloader");
                pd.setMessage("Downloading...");
                pd.setIndeterminate(false);
                pd.show();
            }

We then override the doInBackground(). This is a must since it's here that our background image downloading will be taking place in a separate thread.

@Override
        protected Bitmap doInBackground(String... url) {|

You can see it's returning a Bitmap while taking in a string url. Here's how we download the image. Add the following code inside your doInBackground() method:

          String myurl=url[0];
          Bitmap bm=null;
          InputStream is=null;

          try
          {
            is=new URL(myurl).openStream();

            //DECODE
            bm=BitmapFactory.decodeStream(is);

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

          return bm;

Then lastly we override the onPostExecute() a method that gets executed when the doInBackground has returned. We set our bitmap to our imageview here and clear the progress dialog.

@Override
        protected void onPostExecute(Bitmap result) {
          // TODO Auto-generated method stub
          super.onPostExecute(result);

          //SET TO IMG
          img.setImageBitmap(result);

           //DISMISS
          pd.dismiss();

        }

Now let's go back inside our MainActivity and override it's onCreate() method:

  @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

Then refernce our widgets: imageview and download button:

            img=(ImageView) findViewById(R.id.imageView1);
            downloadBtn=(Button) findViewById(R.id.button);

Then inside our download button's onClickListener execute our AsyncTask, passing in the URL:

          @Override
          public void onClick(View arg0) {
            // TODO Auto-generated method stub
            new Downloader().execute(url);
          }
        });

2. AndroidMainfest.xml

Then make sure you add the permission to internet in your androidmanifest.xml.

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.tutorials.asyncimagedownload"
        android:versionCode="1"
        android:versionName="1.0" >

        <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.tutorials.asyncimagedownload.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>

3. Our XML Layout

Android apps user interface are written in XML. So we need to write our layout in XML as well. Our root layout tag is going to be a relativelayout. We add a button and an imageview inside it. Just Add ImageView and button to your xml layout.


    <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >

        <Button
            android:id="@+id/button"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:background="#009968"
            android:layout_marginBottom="58dp"
            android:text="Download" />

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/imageView1"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="43dp"
            android:src="@drawable/imageloading" />

    </RelativeLayout>

  And that's it.

Comments