Reprinted please indicate the source: http://blog.csdn.net/guolin_blog/article/details/9316683
The main content of this article is Android Doc. I have done some processing after translation, and good English friends can directly read the original text.
Http://developer.android.com/training/displaying-bitmaps/index.html
Efficient loading of large images
Many images are often used when writing Android programs. Different images have different shapes and sizes, but in most cases, these images will be larger than the size required by our program. For example, most of the images displayed in the System Image Library are taken by mobile phone cameras. The resolution of these images is much higher than that of our mobile phone screen. We should know that all the applications we write have certain memory limitations. If the program occupies too much memory, it is prone to oom (outofmemory) exceptions. The following code shows the maximum memory available for each application.
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);Log.d("TAG", "Max memory is " + maxMemory + "KB");
Therefore, it is best to compress the image before displaying a high-resolution image. The size of the compressed image should be similar to the size of the control used to display it. displaying a large image on a small imageview will not bring any visual benefits, but it will occupy a lot of our valuable memory, and may have a negative impact on performance. Next, let's take a look at how to properly compress a large image so that it can be displayed in the optimal size while preventing the emergence of OOM.
The bitmapfactory class provides multiple parsing methods (decodebytearray, decodefile, decoderesource, etc.) for creating bitmap objects. We should select an appropriate method based on the image source. For example, the decodefile method can be used for images on the SD card, the decodestream method can be used for images on the network, and the decoderesource method can be used for images in resource files. These methods will try to allocate memory for the constructed bitmap, which will easily lead to oom. Therefore, each parsing method provides an optional bitmapfactory. the options parameter. If the injustdecodebounds attribute of this parameter is set to true, the resolution method cannot allocate memory to bitmap. The returned value is no longer a bitmap object, but null. Although bitmap is null, The outwidth, outheight, and outmimetype attributes of bitmapfactory. Options are assigned values. This technique allows us to obtain the length and width of the image and the MIME type before loading the image, so as to compress the image as needed. The following code is used:
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;
To avoid OOM exceptions, it is best to check the size of each image when parsing it, unless you trust the source of the image to ensure that these images will not exceed the available memory of your program.
Now that we know the image size, we can decide whether to load the entire image into the memory or a compressed image into the memory. We need to consider the following factors:
- Estimate the memory used to load the entire image.
- The amount of memory you are willing to provide to load this image.
- The actual size of the widget used to display the image.
- The screen size and resolution of the current device.
For example, your imageview only has a size of 128*96 pixels, just to display a thumbnail. It is obviously not worthwhile to load a 1024*768 pixel image into the memory.
How can we compress images? You can set the value of insamplesize in bitmapfactory. Options. For example, if we have a 2048*1536 pixel image and set the value of insamplesize to 4, we can compress the image to 512*384 pixels. Originally, loading this image requires 13 MB of memory. After compression, only 0.75m is needed. (assume that the image is of the argb_8888 type, that is, each pixel occupies 4 bytes ). The following method calculates the appropriate insamplesize value based on the input width and height:
Public static int calculateinsamplesize (bitmapfactory. options options, int reqwidth, int reqheight) {// the height and width of the source image final int Height = options. outheight; Final int width = options. outwidth; int insamplesize = 1; if (height> reqheight | width> reqwidth) {// calculate the final int heightratio = math. round (float) height/(float) reqheight); Final int widthratio = math. round (float) width/(float) reqw Idth); // The ratio of width to the minimum of height is used as the value of insamplesize. This ensures that the width and height of the final image are always greater than or equal to the width and height of the target. Insamplesize = heightratio <widthratio? Heightratio: widthratio;} return insamplesize ;}
To use this method, set the injustdecodebounds attribute of bitmapfactory. Options to true and parse the image once. Then pass bitmapfactory. options together with the expected width and height to the calculateinsamplesize method to obtain the appropriate insamplesize value. Then parse the image again, use the newly obtained insamplesize value, and set injustdecodebounds to false to get the compressed image.
Public static bitmap decodesampledbitmapfromresource (Resources res, int resid, int reqwidth, int reqheight) {// set injustdecodebounds to true for the first resolution to obtain the final bitmapfactory image size. options = new bitmapfactory. options (); options. injustdecodebounds = true; bitmapfactory. decoderesource (Res, resid, options); // call the method defined above to calculate the insamplesize value options. insamplesize = calculateinsamplesize (options, reqwidth, reqheight); // use the obtained insamplesize value to parse the image options again. injustdecodebounds = false; return bitmapfactory. decoderesource (Res, resid, options );}
The following code compresses an image into a 100*100 thumbnail and displays it on the imageview.
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Image Cache Technology
Loading an image on the UI of your application is very simple, but the situation becomes complicated when you need to load a lot of images on the UI. In many cases, (for example, using components such as listview, gridview, or viewpager), images displayed on the screen can be continuously increased by sliding the screen and other events, resulting in oom.
To ensure that the memory usage is always within a reasonable range, images removed from the screen are usually recycled. At this time, the garbage collector will think that you no longer hold the reference of these images, and then perform GC operations on these images. It is very good to solve the problem with this idea. However, to make the program run quickly and load images quickly on the interface, you must consider that after some images are recycled, the user slides it back into the screen. Reload the image you just loaded is undoubtedly a performance bottleneck. You need to find a way to avoid this situation.
At this time, the memory cache technology can be used to solve this problem, which allows components to quickly reload and process images. Next, let's take a look at how to use the memory cache technology to cache images so that your applications can increase response speed and smoothness when loading many images.
The memory cache technology provides quick access to images that occupy a large amount of valuable memory of applications. The core class here is lrucache (which is provided in the android-support-v4 package ). This class is very suitable for caching images. Its main algorithm principle is to store recently used objects with strong references in javashashmap, in addition, the minimum recently used objects are removed from the memory before the cache value reaches the preset value.
In the past, we often used the implementation of a very popular memory cache technology, namely, softreference or weakreference ). However, this method is no longer recommended, because since Android 2.3 (API level 9), the garbage collector prefers to recycle objects that hold soft or weak references, this makes soft references and weak references no longer reliable. In addition, in Android 3.0 (API level 11), image data is stored in the local memory, so it cannot be released in a foreseeable way, this poses a potential risk of application memory overflow and crash.
To select an appropriate cache size for lrucache, consider the following factors:
- How much memory can your device allocate to each application?
- How many images can be displayed at a time on the device screen? How many images need to be preloaded because they may be displayed on the screen soon?
- What is the screen size and resolution of your device? A super high-resolution device (such as Galaxy Nexus) requires a larger cache space when holding the same number of images than a low-resolution device (such as Nexus S.
- The size and size of the image, and the memory size occupied by each image.
- How often are images accessed? Will some images be accessed more frequently than other images? If so, you may need to keep some images in the memory, or use multiple lrucache objects to differentiate different groups of images.
- Can you maintain a balance between quantity and quality? In some cases, it is more effective to store multiple low-pixel images, while loading high-pixel images in the background with a thread.
No specified cache size can satisfy all applications, which is determined by you. You should analyze the usage of program memory and then develop a suitable solution. A small cache space may cause frequent image release and reload, which is not advantageous. A large cache space may cause exceptions in Java. Lang. outofmemory.
The following is an example of using lrucache to cache images:
Private lrucache <string, bitmap> mmemorycache; @ overrideprotected void oncreate (bundle savedinstancestate) {// gets the maximum available memory. exceeding this value will cause an outofmemory exception. // Lrucache transmits the cache value through the constructor, in KB. Int maxmemory = (INT) (runtime. getruntime (). maxmemory ()/1024); // use 1/8 of the maximum available memory value as the cache size. Int cachesize = maxmemory/8; mmemorycache = new lrucache <string, bitmap> (cachesize) {@ overrideprotected int sizeof (string key, Bitmap bitmap) {// rewrite this method to measure the size of each image. By default, the number of images is returned. Return bitmap. getbytecount ()/1024 ;}};} public void addbitmaptomemorycache (string key, Bitmap bitmap) {If (getbitmapfrommemcache (key) = NULL) {mmemorycache. put (Key, bitmap) ;}} public bitmap getbitmapfrommemcache (string key) {return mmemorycache. get (key );}
In this example, 1/8 of the memory allocated to the application is used as the cache size. Among the mobile phones with medium and high configurations, there will be approximately 4 MB (32/8) of cache space. A full-screen gridview is filled with four 1.5x800 images, which occupies about 480 MB of space (* 4 ). Therefore, the cache size can store images on 2.5 pages.
When you load an image to the imageview, it is first checked in the cache of lrucache. If the corresponding key value is found, the imageview is updated immediately. Otherwise, a background thread is enabled to load the image.
public void loadBitmap(int resId, ImageView imageView) {final String imageKey = String.valueOf(resId);final Bitmap bitmap = getBitmapFromMemCache(imageKey);if (bitmap != null) {imageView.setImageBitmap(bitmap);} else {imageView.setImageResource(R.drawable.image_placeholder);BitmapWorkerTask task = new BitmapWorkerTask(imageView);task.execute(resId);}}
Bitmapworkertask also places the key-value pairs of the newly loaded image into the cache.
Class bitmapworkertask extends asynctask <integer, void, bitmap> {// load the image in the background. @ Overrideprotected bitmap doinbackground (integer... params) {final Bitmap bitmap = decodesampledbitmapfromresource (getresources (), Params [0], 100,100); addbitmaptomemorycache (string. valueof (Params [0]), bitmap); Return bitmap ;}}
Master the above two methods, whether it is to load large images in the program, or to load a large number of images, do not worry about the OOM problem! However, I just want to explain it theoretically and I don't know if you can fully understand it. In the subsequent articles, I will demonstrate how to use the above techniques flexibly in actual programs to avoid program oom, if you are interested, please continue to read the android photo wall application implementation, and you will not be afraid of crashes if you have more images.