Loading an Image from Internet (URL), in Android

One of the common tasks in Android is to download and display an image from the internet given the image URL. In this tutorial we will see how this task can be accomplished successfully.
But before we dive into implementation details, lets quickly walk through a few important aspects when dealing with network operations.

The Main Thread and its Relation with the Network

Every time an application is launched, the system creates a thread called "main" (also know as the "UI thread"). The role of the main thread is to dispatch events to the appropriate views. Performing long operations on the main thread (like downloading a file from the internet) will block the user interface during that operation. In Android, the system guards against applications that are insufficiently responsive for a period of time by displaying a dialog that says that your app has stopped responding, offering the user an option to quit the app. I am assuming you don't want this to happen in your app.

Moreover, starting with Android 3.0 (Honeycomb) and above, you are not allowed anymore to perform network operations on the main thread. Doing so will raise the famous NetworkOnMainThreadException.

Fortunately, Android provides the AsyncTask class that can perform tasks on a separate thread.

A short intro to AsyncTask

The AsyncTask class enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

In order to use it, you must extend AsyncTask and override at least 1 method, the doInBackground() method.
However, the most common method that you will use (and are used in this tutorial) are:

1. onPreExecute() – called on the UI thread before the thread starts running. This method is usually used to setup the task, for example by displaying a progress bar.

2. doInBackground(Params…) – this is the method that runs on the background thread. In this method you should put all the code you want the application to perform in background, like making the actual http request to download the image.

3. onPostExecute(Result) – called on the UI thread after the background thread finishes. It takes as parameter the result received from doInBackground(). Usually used to update the user interface, like displaying the image that was downloaded.

Example

The following code demonstrates how to use AsyncTask to download an image from the internet.

1. Adding the INTERNET permission
Add the INTERNET permission to the AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET" />
(Also, make sure your device is actually connected to the internet, otherwise you may run into UnknownHostException)

2. The XML layout file
The layout file defines the Button that will start the download task, an ImageView where the result will be displayed, and a ProgressBar that will be shown while the image is downloading.
Note that the ProgressBar is hidden by default (visibility="gone"). We will programmatically show it when the download will start and hide when the download will finish.
<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:orientation="vertical" >

    <Button
        android:id="@+id/load_image_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="6dp"
        android:text="@string/load_image" />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/load_image_button"
        android:scaleType="centerCrop"
        tools:ignore="ContentDescription" />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:visibility="gone" />

</RelativeLayout>

3. The TaskListener interface
Create an interface with the following content:
public interface TaskListener {
   void onTaskStarted();

   void onTaskFinished(Bitmap bitmap);
}
This interface will be used to communicate between AsyncTask and your Activity. Using an interface for communication is a good practice, as it decouples dependencies between your classes. For example you will be able to use your implementation of AsyncTask in different activities and even different projects.

onTaskStarted() - will be called before starting the download. Here the ProgressBar will be set as visible.

void onTaskFinished(Bitmap bitmap) - will be called when the download will finish, giving the result into a bitmap image. Here the ProgressBar will be hidden and the image displayed.

4. The ImageLoadingTask class
Create a class called ImageLoadingTask with the following content:
public class ImageLoadingTask extends AsyncTask<String, Void, Bitmap> {

   private TaskListener listener;

   public ImageLoadingTask(TaskListener listener) {
      this.listener = listener;
   }
 
   @Override
   protected void onPreExecute() {
      listener.onTaskStarted();
   }

   @Override
   protected Bitmap doInBackground(String... params) {
      Bitmap bitmap = null;
      try {
         URLConnection connection = new URL(params[0]).openConnection();
         InputStream response = connection.getInputStream();
         bitmap = BitmapFactory.decodeStream(response);
      } catch (IOException e) {
         e.printStackTrace();
      }

      return bitmap;
   }
 
   @Override
   protected void onPostExecute(Bitmap result) {
      listener.onTaskFinished(result);
   }

}

Here is what the parameterized types AsyncTask<String, Void, Bitmap> mean:
String - this AsyncTask will be given a String, the image url.
Void - we don't care about this one, but it is used when publishing updates to UI from the task.
Bitmap - the output, the downloaded image.

The onPreExecute()/onPostExecute() do nothing but delegate the work to the calling activity by calling the listener methods:
@Override
protected void onPreExecute() {
   listener.onTaskStarted();
}

@Override
protected void onPostExecute(Bitmap result) {
   listener.onTaskFinished(result);
}
And the core job is done in doInBackground():
// Open the connection for the specified url
URLConnection connection = new URL(params[0]).openConnection();

// Get the input stream
InputStream response = connection.getInputStream();

// Create a Bitmap from the input stream
bitmap = BitmapFactory.decodeStream(response);

5. The Activity class
The activity class implements the TaskListener interface and provides implementations for the onTasStarted()/onTaskFinished(Bitmap) methods.
Also, when the button is clicked the AsyncTask is executed.
public class NetworkPictureActivity extends ActionBarActivity implements TaskListener {

   private ProgressBar progressBar;
   private ImageView imageView;

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

      progressBar = (ProgressBar) findViewById(R.id.progress_bar);
      imageView = (ImageView) findViewById(R.id.image_view);

      Button loadImageButton = (Button) findViewById(R.id.load_image_button);
      loadImageButton.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            new ImageLoadingTask(NetworkPictureActivity.this)
                .execute("http://i.imgur.com/9gbQ7YR.jpg"); 
         }
      });
   }

   @Override
   public void onTaskStarted() {
      progressBar.setVisibility(View.VISIBLE);
   }

   @Override
   public void onTaskFinished(Bitmap bitmap) {
      progressBar.setVisibility(View.GONE);
      imageView.setImageBitmap(bitmap);  
   }
}

Niciun comentariu:

Trimiteți un comentariu