To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it:
BitmapFactory.Options options = new BitmapFactory.Options(); //Setting true while decoding avoids memory allocation options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize in your BitmapFactory. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image.
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw 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; } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //not load to memory BitmapFactory.decodeResource(res, resId, options); //returning null // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false;//load to memory for actually decode return BitmapFactory.decodeResource(res, resId, options); }
//How to use: mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
When decode the image, speed depend on reading from disk or network, size of image, power of CPU. AsyncTask is an typical way to handle this:
class BitmapWorkerTask extends AsyncTask{ private final WeakReference imageViewReference; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference (imageView); } @Override protected Bitmap doInBackground(Integer... params) { return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
An example from android blog to handle loading bitmap into listview using AsyncTask: Multithreading for Performance
Personal example to loading thumbnails from internet: Image downloader in List/Recycle View
Create a dedicated Drawable subclass to store a reference back to the worker task. In this case, a BitmapDrawable is used so that a placeholder image can be displayed in the ImageView while the task completes:
static class AsyncDrawable extends BitmapDrawable { private final WeakReferencebitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference (bitmapWorkerTask); } } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); } } public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; // If bitmapData is not yet set or it differs from the new data if (bitmapData == 0 || bitmapData != data) { // Cancel previous task bitmapWorkerTask.cancel(true); } else { // The same work is already in progress return false; } } // No task associated with the ImageView, or an existing task was cancelled return true; } private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; } class BitmapWorkerTask extends AsyncTask { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }