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