1page.title=Loading Large Bitmaps Efficiently
2parent.title=Displaying Bitmaps Efficiently
3parent.link=index.html
4
5trainingnavtop=true
6next.title=Processing Bitmaps Off the UI Thread
7next.link=process-bitmap.html
8
9@jd:body
10
11<div id="tb-wrapper">
12<div id="tb">
13
14<h2>This lesson teaches you to</h2>
15<ol>
16  <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li>
17  <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li>
18</ol>
19
20<h2>Try it out</h2>
21
22<div class="download-box">
23  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
24  <p class="filename">BitmapFun.zip</p>
25</div>
26
27</div>
28</div>
29
30<p>Images come in all shapes and sizes. In many cases they are larger than required for a typical
31application user interface (UI). For example, the system Gallery application displays photos taken
32using your Android devices's camera which are typically much higher resolution than the screen
33density of your device.</p>
34
35<p>Given that you are working with limited memory, ideally you only want to load a lower resolution
36version in memory. The lower resolution version should match the size of the UI component that
37displays it. An image with a higher resolution does not provide any visible benefit, but still takes
38up precious memory and incurs additional performance overhead due to additional on the fly
39scaling.</p>
40
41<p>This lesson walks you through decoding large bitmaps without exceeding the per application
42memory limit by loading a smaller subsampled version in memory.</p>
43
44<h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2>
45
46<p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link
47android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
48decodeByteArray()}, {@link
49android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options)
50decodeFile()}, {@link
51android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options)
52decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose
53the most appropriate decode method based on your image data source. These methods attempt to
54allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory}
55exception. Each type of decode method has additional signatures that let you specify decoding
56options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link
57android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding
58avoids memory allocation, returning {@code null} for the bitmap object but setting {@link
59android.graphics.BitmapFactory.Options#outWidth}, {@link
60android.graphics.BitmapFactory.Options#outHeight} and {@link
61android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the
62dimensions and type of the image data prior to construction (and memory allocation) of the
63bitmap.</p>
64
65<pre>
66BitmapFactory.Options options = new BitmapFactory.Options();
67options.inJustDecodeBounds = true;
68BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
69int imageHeight = options.outHeight;
70int imageWidth = options.outWidth;
71String imageType = options.outMimeType;
72</pre>
73
74<p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before
75decoding it, unless you absolutely trust the source to provide you with predictably sized image data
76that comfortably fits within the available memory.</p>
77
78<h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2>
79
80<p>Now that the image dimensions are known, they can be used to decide if the full image should be
81loaded into memory or if a subsampled version should be loaded instead. Here are some factors to
82consider:</p>
83
84<ul>
85  <li>Estimated memory usage of loading the full image in memory.</li>
86  <li>Amount of memory you are willing to commit to loading this image given any other memory
87  requirements of your application.</li>
88  <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image
89  is to be loaded into.</li>
90  <li>Screen size and density of the current device.</li>
91</ul>
92
93<p>For example, itâs not worth loading a 1024x768 pixel image into memory if it will eventually be
94displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p>
95
96<p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link
97android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link
98android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that
99is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
100bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
101image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Hereâs
102a method to calculate a the sample size value based on a target width and height:</p>
103
104<pre>
105public static int calculateInSampleSize(
106            BitmapFactory.Options options, int reqWidth, int reqHeight) {
107    // Raw height and width of image
108    final int height = options.outHeight;
109    final int width = options.outWidth;
110    int inSampleSize = 1;
111
112    if (height > reqHeight || width > reqWidth) {
113        if (width > height) {
114            inSampleSize = Math.round((float)height / (float)reqHeight);
115        } else {
116            inSampleSize = Math.round((float)width / (float)reqWidth);
117        }
118    }
119    return inSampleSize;
120}
121</pre>
122
123<p class="note"><strong>Note:</strong> Using powers of 2 for {@link
124android.graphics.BitmapFactory.Options#inSampleSize} values is faster and more efficient for the
125decoder. However, if you plan to cache the resized versions in memory or on disk, itâs usually still
126worth decoding to the most appropriate image dimensions to save space.</p>
127
128<p>To use this method, first decode with {@link
129android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options
130through and then decode again using the new {@link
131android.graphics.BitmapFactory.Options#inSampleSize} value and {@link
132android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p>
133
134<a name="decodeSampledBitmapFromResource"></a>
135<pre>
136public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
137        int reqWidth, int reqHeight) {
138
139    // First decode with inJustDecodeBounds=true to check dimensions
140    final BitmapFactory.Options options = new BitmapFactory.Options();
141    options.inJustDecodeBounds = true;
142    BitmapFactory.decodeResource(res, resId, options);
143
144    // Calculate inSampleSize
145    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
146
147    // Decode bitmap with inSampleSize set
148    options.inJustDecodeBounds = false;
149    return BitmapFactory.decodeResource(res, resId, options);
150}
151</pre>
152
153<p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link
154android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example
155code:</p>
156
157<pre>
158mImageView.setImageBitmap(
159    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
160</pre>
161
162<p>You can follow a similar process to decode bitmaps from other sources, by substituting the
163appropriate {@link
164android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
165BitmapFactory.decode*} method as needed.</p>