Exploring Android BitmapFactory

BitmapFactory is a class in Android that allows you to create Bitmap objects from various sources, such as files, streams, resources, and byte arrays. In this tutorial we will take a look on what are the most often used methods of this class and which scenarios call to use one method over another.

The BitmapFactory class has several decode* static methods, that differ only in the input source. The most often used are:
Bitmap decodeResource(Resources res, int id)
Bitmap decodeResource(Resources res, int id, BitmapFactory.Options opts)

Bitmap decodeFile(String pathName)
Bitmap decodeFile(String pathName, BitmapFactory.Options opts)

Bitmap decodeByteArray(byte[] data, int offset, int length)
Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts)

Bitmap decodeStream(InputStream is)
Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts)
Each of the above methods comes in two variants: one that takes as parameter a BitmapFactory.Options, and one that does not. The BitmapFactory.Options let you specify decoding options. One common scenario when is needed to specify decoding options, (that we will illustrate later), is when loading a scaled down version of the bitmap in order to prevent OutOfMemoryError.

But for the moment lets walk though the above mentioned methods and see where it is appropriate to use them.

1. BitmapFactory.decodeResource, can be used when the image needed to display is located in drawable resource folders.
Bitmap bitmap = BitmapFactory.decodeResource(
                       getResources(), R.drawable.image);
imageView.setImageBitmap(bitmap);
The method accepts the Resources instance and the drawable resource id.

2. BitmapFactory.decodeFile, useful when the image source can be represented as a File object, for example when needed to load an image from the sd card.
File file = new File(Environment
               .getExternalStorageDirectory() + "/Pictures/image.jpg");
Bitmap bitmap = BitmapFactory.decodeFile(file);
imageView.setImageBitmap(bitmap);

3. BitmapFactory.decodeStream is a universal method for loading bitmaps, as any readable source of bytes can be read as an input stream.
In the example below an image is downloaded from the internet and the response is got as InputStream which is converted into a Bitmap.
URLConnection connection = 
            new URL("http://i.imgur.com/9gbQ7YR.jpg").openConnection();
InputStream response = connection.getInputStream();

Bitmap bitmap = BitmapFactory.decodeStream(response)
imageView.setImageBitmap(bitmap);

4. BitmapFactory.decodeByteArray, used to create a bitmap from a byte array. There may be times when the image to be displayed will be available to you as a byte array. One common scenario is when you have the base 64 image representation as a String. In this case, the base 64 string is converted to a byte array and passed to BitmapFactory.decodeByteArray.
String base64String = "...";
byte[] imageData = Base64.decode(base64String, Base64.DEFAULT);

Bitmap bitmap = BitmapFactory
               .decodeByteArray(imageData, 0, imageData.length);
imageView.setImageBitmap(bitmap);

Specifying decoding options to prevent OutOfMemoryError

Working with images in Android is probably one of the few areas but most often encountered, that "remembers" you that the memory available to your application is limited. Usually, the solution to this issue is to subsample the image, or with other words, to load a smaller version into memory. For example, it is not worth loading a 1024 x 500 pixel image, only to be displayed in a 120 x 90 thumbnail image.
And this is the moment where the BitmapFactory.Options enters the scene.

Two of the public fields of Options class are of interest to us in order to subsample the image:

public boolean inJustDecodeBounds - if is set to true, the decoder will return null (no bitmap), but the out fields of the Options instance will be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.

This "... query the bitmap without having to allocate the memory", is the most important part you should remember in regards to inJustDecodeBounds field.

public int inSampleSize - if set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.

For example inSampleSize = 4 will scale a 1024 x 500 pixel image, to 256 x 125 pixel image, which is way more acceptable in terms of memory consumption than the original image.

Example:
Bitmap bitmap = scaleBitmapFromResource(R.drawable.image, 250, 120);
imageView.setImageBimtap(bitmap);
public Bitmap scaleBitmapFromResource(int resId, int reqWidth, int reqHeight){
   BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeResource(res, resId, options);

   // Calculate inSampleSize
   options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

   // Decode bitmap with inSampleSize set
   options.inJustDecodeBounds = false;
   return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Original height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2
        // and keeps both height and width larger 
        // than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

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);  
   }
}

Taking Pictures with Camera in Android

Taking pictures with camera follows the same approach that was used when picking an image from gallery. Basically, your activity starts the Camera application, you take a photo and save it. Once the photo is saved you will be redirected back to your app where you have the possibility to load and display the taken photo.

How Taking Pictures with Camera works

To start the Camera application, an Intent with the action ACTION_IMAGE_CAPTURE is fired. Also, this Intent is given a Uri where the picture will be saved to. The Uri basically is the location where the image will be stored on your device after the photo is taken. To get a result from the started activity, startActivityForResult() is used, instead of startActivity().
private void takePhoto() {
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   intent.putExtra(MediaStore.EXTRA_OUTPUT, 
                   Uri.fromFile(getImageFile()));
   startActivityForResult(intent, TAKE_PHOTO);
}

getImageFile() returns the file where the picture content will be saved to.
private File getImageFile() {
   return new File(Environment.getExternalStorageDirectory(), 
                   "capture.png");
}
In the example above the picture will be saved in the root directory of the external storage, in a file named capture.png. Later you can use any file manager application to browse and view the photo.

Besides the Intent to start, startActivityForResult() takes a second parameter, the request code which is an integer number (TAKE_PHOTO), that will be used to identify your request in onActivityResult() callback.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   FileInputStream inputStream = null; 
   // If the user finished the action
   // and the request code is your request code
   if (resultCode == RESULT_OK && requestCode == TAKE_PHOTO) {
     try {
       // Read the content of the picture into an InputStream
       inputStream = new FileInputStream(getImageFile());

       // Decode the stream and create a Bitmap from it
       Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

       // Set the image Bitmap
       imageView.setImageBitmap(bitmap);
     } catch (FileNotFoundException e) {
        Log.e("TAG", "Cant load image", e);
     } finally {
        // close resources
        closeStream(inputStream);
     }
   }
}


Below follows the complete source code of the XML layout and activity class.
The content of the main XML layout:
<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"
    tools:ignore="HardcodedText" >

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

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

</LinearLayout>

Full source code of the activity that manages taking and displaying the picture:
public class PhotoCameraActivity extends ActionBarActivity {

   private static final int TAKE_PHOTO = 101;
   private ImageView imageView;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_photo_camera);
 
      imageView = (ImageView) findViewById(R.id.image_view);
      Button takePhotoBtn = (Button) findViewById(R.id.take_photo);
      takePhotoBtn.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            takePhoto();
         }
      });
   }

   private void takePhoto() {
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, 
                      Uri.fromFile(getImageFile()));
      startActivityForResult(intent, TAKE_PHOTO);
   }

   private File getImageFile() {
      return new File(Environment.getExternalStorageDirectory(), 
                      "capture.png");
   }

   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      FileInputStream inputStream = null; 
      if (resultCode == RESULT_OK && requestCode == TAKE_PHOTO) {
         try {
            inputStream = new FileInputStream(getImageFile());
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
            imageView.setImageBitmap(bitmap);
         } catch (FileNotFoundException e) {
            Log.e("TAG", "Cant load image", e);
         } finally {
            closeStream(inputStream);
         }
      }
   }

   private void closeStream(FileInputStream inputStream) {
      if (inputStream != null) {
         try {
            inputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

}

Checking the device has a camera

Although nowadays most of mobile devices come equipped with a camera, you never can be sure on what devices your android app will run. For example, not all tablets have a camera.
Therefore, before starting the camera app you have to make sure that the device has one in place.
You can check that using the hasSystemFeature(PackageManager.FEATURE_CAMERA) method of the PackageManager class.
if (isCameraAvailable()) {
   takePhoto();
} else {
   Toast.makeText(this, "This device does not have a camera", 
                  Toast.LENGTH_LONG).show();
}
// ...
private boolean isCameraAvailable() {
   return getPackageManager()
          .hasSystemFeature(PackageManager.FEATURE_CAMERA)
}

Loading Images in Android (Part III: Pick images from Gallery)

In Part III we will look on how to let the user pick an image from the Android Gallery and display it in your application.
When clicking on the Pick Image button, the Android media gallery will open allowing you to browse for a photo, select it, and finally display it in the application.

Below is the XML layout of the above Activity. It consists of a Button that will trigger the pick action, and an ImageView that will display the selected photo.
<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" >

    <Button
        android:id="@+id/pick_image_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Pick Image" />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

And this is the Activity that manages all the job on picking an displaying the picture.
public class ImagePickerActivity extends ActionBarActivity {

   private static final int PICK_IMAGE = 100;
   private ImageView imageView;

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

      imageView = (ImageView) findViewById(R.id.image_view);

      Button pickImageButton = (Button) findViewById(R.id.pick_button);
      pickImageButton.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            openGallery();
         }
      });
   }

   private void openGallery() {
      Intent gallery = 
         new Intent(Intent.ACTION_PICK, 
         android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
      startActivityForResult(gallery, PICK_IMAGE);
   }

   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      if (resultCode == RESULT_OK && requestCode == PICK_IMAGE) {
         Uri imageUri = data.getData();
         imageView.setImageURI(imageUri);
      }
   }
}

How Picking an Image from Gallery Works

The Gallery which displays the user's photos is a separate application over which we don't have control, in the sense that there isn't an API that will allow us to manipulate the Gallery from inside our app.
However, Android has a mechanism that allows us to get a result from another Activity, even if that activity is not ours. This is accomplished by calling startActivityForResult() (instead of startActivity()), and overriding onActivityResult() to retrieve the result from the previous started Activity.

startActivityForResult() takes two parameters, the Intent to start and a request code.
private static final int PICK_IMAGE = 100;
// ...
private void openGallery() {
   Intent gallery = 
      new Intent(Intent.ACTION_PICK, 
      android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
   startActivityForResult(gallery, PICK_IMAGE);
}

The intent to start can be explicit (when the name of the activity to be started is known, usually used when calling one of your activities) or implicit (when other activities, usually that are not part of your application, can perform the action). In the above example an implicit intent is used.

The "request code" (PICK_IMAGE) is an integer number that identifies your request (doesn't have to be "100", you can use any integer number). When receiving the result in the onActivityResult() callback, the same request code is provided so that your app can properly identify the result and determine how to handle it.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   
   // If the result is OK (user finished the action)
   // and the request code is our code,
   // then this is our request and we can process the result
   if (resultCode == RESULT_OK && requestCode == PICK_IMAGE) {
       // process the result
   }
}

And the result returns the Uri of the selected picture (an Uri can be regarded as the "address" or an identifier of the picture), which is passed to the ImageView to load and display.
Uri imageUri = data.getData();
imageView.setImageURI(imageUri);

Loading Images in Android (Part II: From Assets)

In this article we will look on how we can load images from the assets directory. If you are working in a gradle based project (eg. Android Studio), then you will find the assets directory at: src/main/assets, and in a standard Android project (eg. Eclipse + ADT), assets is located in the project's folder. By default the directory is empty.

In a previous article we examined how to load images from the drawable resource directory. You may ask what are the differences between the two and what benefits bring each of these 2 storage locations.

The benefits of drawable resources is that it provides built-in support for different OS versions, languages, screen orientation, etc. For example you can have normal images in the drawable-mdpi directory, and bigger ones in the drawable-xhdpi directory, then depending of the screen size, the system will pick the image from the appropriate directory. Also, as  shown in a previous article, the images stored in drawables can be referenced directly in the XML files.

None of the above mentioned benefits are available for assets. The assets directory can be used to store raw asset files. Files that you save here are compiled into an .apk file as-is, and the original filename is preserved. You can navigate this directory in the same way as a typical file system using URIs and read files as a stream of bytes using the AssetManager. For example, this is a good location for textures and game data.

To load an image from assets, we could use the getAssets().open(path) method of the Activity class. This will return an InputStream that passed to BitmapFactory.decodeStream(inputStream), will give us the image.
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_photo_loading);

    ImageView imageView = (ImageView) findViewById(R.id.image_view);

    try {
        InputStream inputStream = getAssets().open("image.png");
        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
        imageView.setImageBitmap(bitmap);
    } catch (IOException e) {
        Log.d("TAG", "Could not load the image");
    }
}

Loading images in Android (Part I: From Drawables and from Local Storage)

In these series of tutorials you will learn various ways of loading and displaying an image depending of image source. Specifically, we will look into:
  1. Loading an image from resource directory
  2. Loading an image from local storage
  3. Loading an image from assets directory
  4. Picking an image from Gallery
  5. Loading an image taken with Camera
  6. Loading an image from network
    1. Using AsyncTask
    2. Using Picasso image loading library
In Part I we will examine loading an image from resource and local storage.

Loading an image from drawable resource directory.

Loading an image from drawable directory is one of the simplest and common use cases. You use this when you have static images bundled within your application.

Assuming an image is located at: res/drawable/image.png, you can load it using the following methods:

a) directly in the XML file:
<ImageView
    android:id="@+id/image_view"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:src="@drawable/image" />
The src attribute is taking the resource type: @drawable/ and the resource id: image (which is the file name without extention).

b) programmatically:
// Get a reference to the ImageView
ImageView imageView = (ImageView)findViewById(R.id.image_view);

// Set the image 
imageView.setImageResource(R.drawable.image);
The programmatic method is useful in scenarios where the image to be displayed is to be decided at runtime, for example in response to a particular user action.

Loading an image from local storage.

Assuming that an image is located in the phone's external storage, in a directory called Pictures, it can be loaded using the static method decodeFile(path) of the class BitmapFactory.
ImageView imageView = (ImageView) findViewById(R.id.image_view);

Bitmap bitmap = BitmapFactory.decodeFile(
                   Environment.getExternalStorageDirectory()
                   + "/Pictures/image.png");

if (bitmap != null) {
   imageView.setImageBitmap(bitmap);
} else {
   Toast.makeText(this, "Can't load image", Toast.LENGTH_SHORT).show();
}

Note that if you will try to load large images, you may run into the famous OutOfMemoryError. Normally, displaying an image involves some preprocessing, like scaling down the image before loading it in memory, and this is the subject of the next article.