| page.title=Loading Large Bitmaps Efficiently | |
| parent.title=Displaying Bitmaps Efficiently | |
| parent.link=index.html | |
| trainingnavtop=true | |
| @jd:body | |
| <div id="tb-wrapper"> | |
| <div id="tb"> | |
| <h2>This lesson teaches you to</h2> | |
| <ol> | |
| <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li> | |
| <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li> | |
| </ol> | |
| <h2>Try it out</h2> | |
| <div class="download-box"> | |
| <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a> | |
| <p class="filename">DisplayingBitmaps.zip</p> | |
| </div> | |
| </div> | |
| </div> | |
| <p>Images come in all shapes and sizes. In many cases they are larger than required for a typical | |
| application user interface (UI). For example, the system Gallery application displays photos taken | |
| using your Android devices's camera which are typically much higher resolution than the screen | |
| density of your device.</p> | |
| <p>Given that you are working with limited memory, ideally you only want to load a lower resolution | |
| version in memory. The lower resolution version should match the size of the UI component that | |
| displays it. An image with a higher resolution does not provide any visible benefit, but still takes | |
| up precious memory and incurs additional performance overhead due to additional on the fly | |
| scaling.</p> | |
| <p>This lesson walks you through decoding large bitmaps without exceeding the per application | |
| memory limit by loading a smaller subsampled version in memory.</p> | |
| <h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2> | |
| <p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link | |
| android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options) | |
| decodeByteArray()}, {@link | |
| android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options) | |
| decodeFile()}, {@link | |
| android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options) | |
| decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose | |
| the most appropriate decode method based on your image data source. These methods attempt to | |
| allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory} | |
| exception. Each type of decode method has additional signatures that let you specify decoding | |
| options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link | |
| android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding | |
| avoids memory allocation, returning {@code null} for the bitmap object but setting {@link | |
| android.graphics.BitmapFactory.Options#outWidth}, {@link | |
| android.graphics.BitmapFactory.Options#outHeight} and {@link | |
| android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the | |
| dimensions and type of the image data prior to construction (and memory allocation) of the | |
| bitmap.</p> | |
| <pre> | |
| BitmapFactory.Options options = new BitmapFactory.Options(); | |
| options.inJustDecodeBounds = true; | |
| BitmapFactory.decodeResource(getResources(), R.id.myimage, options); | |
| int imageHeight = options.outHeight; | |
| int imageWidth = options.outWidth; | |
| String imageType = options.outMimeType; | |
| </pre> | |
| <p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before | |
| decoding it, unless you absolutely trust the source to provide you with predictably sized image data | |
| that comfortably fits within the available memory.</p> | |
| <h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2> | |
| <p>Now that the image dimensions are known, they can be used to decide if the full image should be | |
| loaded into memory or if a subsampled version should be loaded instead. Here are some factors to | |
| consider:</p> | |
| <ul> | |
| <li>Estimated memory usage of loading the full image in memory.</li> | |
| <li>Amount of memory you are willing to commit to loading this image given any other memory | |
| requirements of your application.</li> | |
| <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image | |
| is to be loaded into.</li> | |
| <li>Screen size and density of the current device.</li> | |
| </ul> | |
| <p>For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be | |
| displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p> | |
| <p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link | |
| android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link | |
| android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that | |
| is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a | |
| bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full | |
| image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Here’s | |
| a method to calculate a sample size value that is a power of two based on a target width and | |
| height:</p> | |
| <pre> | |
| 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; | |
| } | |
| </pre> | |
| <p class="note"><strong>Note:</strong> A power of two value is calculated because the decoder uses | |
| a final value by rounding down to the nearest power of two, as per the {@link | |
| android.graphics.BitmapFactory.Options#inSampleSize} documentation.</p> | |
| <p>To use this method, first decode with {@link | |
| android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options | |
| through and then decode again using the new {@link | |
| android.graphics.BitmapFactory.Options#inSampleSize} value and {@link | |
| android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p> | |
| <a name="decodeSampledBitmapFromResource"></a> | |
| <pre> | |
| 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; | |
| 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); | |
| } | |
| </pre> | |
| <p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link | |
| android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example | |
| code:</p> | |
| <pre> | |
| mImageView.setImageBitmap( | |
| decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); | |
| </pre> | |
| <p>You can follow a similar process to decode bitmaps from other sources, by substituting the | |
| appropriate {@link | |
| android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options) | |
| BitmapFactory.decode*} method as needed.</p> |