The main content of this article comes from Android Doc, I have done some processing after translation, English good friends can also directly read the original text.
Http://developer.android.com/training/displaying-bitmaps/index.html
Load large images efficiently
We often use a lot of pictures when we write the Android program, different pictures will always have different shapes, different sizes, but in most cases, these images will be larger than the size of our program. For example, the pictures in the system pictures are mostly taken from the mobile phone camera, the resolution of these images is much higher than the resolution of our phone screen. As you should know, we write applications that have a certain memory limit, and programs that consume too much memory are prone to oom (OutOfMemory) exceptions. We can see how much memory is available for each application using the code below.
1 int maxmemory = (int) (Runtime.getruntime (). MaxMemory ()/1024x768); 2 log.d ("TAG", "Max memory is" + MaxMemory + "KB");
Therefore, when displaying high-resolution images, it is best to compress the images first. The compressed picture size should be similar to the size of the control used to show it, and displaying a large picture on a small imageview will not have any visual benefit, but it will take up quite a lot of our valuable memory and may have a negative impact on performance. Let's take a look at how to properly compress a large picture so that it can be displayed at the best size while preventing Oom from appearing.
The
Bitmapfactory class provides multiple parsing methods (Decodebytearray, DecodeFile, Decoderesource, etc.) for creating bitmap objects, and we should choose the appropriate method based on the source of the image. For example, the image on the SD card can use the DecodeFile method, the picture on the network can use the Decodestream method, the picture in the resource file can use the Decoderesource method. These methods attempt to allocate memory for the bitmap already built, which can easily cause oom to appear. For this reason each parsing method provides an optional bitmapfactory.options parameter that sets the Injustdecodebounds property of this parameter to true to prevent the parsing method from allocating memory for bitmap, and the return value is no longer a bitmap object, but a null 。 Although bitmap is null, Bitmapfactory.options's outwidth, Outheight, and Outmimetype properties are assigned values. This technique allows us to get the image's long and wide values and MIME types before loading the image, thus compressing the image as appropriate. As shown in the following code:
1 New bitmapfactory.options (); 2 true ; 3 Bitmapfactory.decoderesource (Getresources (), r.id.myimage, options); 4 int imageheight = options.outheight; 5 int imagewidth = options.outwidth; 6 String imageType = Options.outmimetype;
To avoid oom anomalies, it's a good idea to check the size of each picture, unless you trust the source of the image so that it doesn't go beyond your program's available memory.
Now that the size of the picture is known, we can decide whether to load the entire picture into memory or load a compressed version of the image into memory. Here are a few things we need to consider:
- Estimate the memory required to load the entire picture.
- How much memory you are willing to provide in order to load this picture.
- The actual size of the control used to show this picture.
- The screen size and resolution of the current device.
For example, your imageview is only 128*96 pixels in size, just to show a thumbnail, it is obviously not worthwhile to load a picture of 1024x768 pixel completely into memory.
So how do we compress the image? This can be achieved by setting the value of Insamplesize in Bitmapfactory.options. For example, we have a picture of 2048*1536 pixels, set the value of Insamplesize to 4, you can compress this image into 512*384 pixels. Originally loading this image needs to occupy 13M of memory, compression will only need to occupy 0.75M (assuming that the image is the argb_8888 type, that is, each pixel occupies 4 bytes). The following method calculates the appropriate insamplesize value according to the width and height of the incoming:
1 Public Static intcalculateinsamplesize (bitmapfactory.options Options,2 intReqwidth,intreqheight) {3 //the height and width of the source picture4 Final intHeight =Options.outheight;5 Final intwidth =Options.outwidth;6 intInsamplesize = 1;7 if(Height > Reqheight | | width >reqwidth) {8 //calculates the ratio between the actual width and the target width9 Final intHeightRatio = Math.Round ((float) Height/(float) reqheight);Ten Final intWidthRatio = Math.Round ((float) Width/(float) reqwidth); One //Select width and high school minimum ratio as the value of the insamplesize, so that the final picture width and height can be guaranteed A //must be greater than or equal to the width and height of the target. -Insamplesize = HeightRatio < WidthRatio?Heightratio:widthratio; - } the returninsamplesize; -}
Using this method, you first set the Injustdecodebounds property of the Bitmapfactory.options to true to parse the picture once. The bitmapfactory.options is then passed along with the desired width and height to the Calculateinsamplesize method, and the appropriate insamplesize value can be obtained. Then parse the picture again, use the newly obtained Insamplesize value, and set the Injustdecodebounds to False, you can get the compressed picture.
1 Public StaticBitmap Decodesampledbitmapfromresource (Resources res,intResId,2 intReqwidth,intreqheight) {3 //The first resolution sets Injustdecodebounds to true to get the picture size4 FinalBitmapfactory.options Options =Newbitmapfactory.options ();5Options.injustdecodebounds =true;6 Bitmapfactory.decoderesource (res, resId, options);7 //call the method defined above to calculate the Insamplesize value8Options.insamplesize =calculateinsamplesize (Options, Reqwidth, reqheight);9 //parse the picture again using the obtained insamplesize valueTenOptions.injustdecodebounds =false; One returnBitmapfactory.decoderesource (res, resId, options); A}
The following code is very simple to compress any picture into a 100*100 thumbnail and show it on the ImageView.
1 Mimageview.setimagebitmap (2 Decodesampledbitmapfromresource (getresources (), R.id.myimage, 100, 100));
Using Picture caching technology
Loading a picture in your application's UI interface is a simple matter, but when you need to load a lot of pictures on the interface, things get complicated. In many cases (such as using widgets such as ListView, GridView, or Viewpager), images displayed on the screen can be continuously increased through events such as sliding screens, eventually resulting in oom.
In order to ensure that the memory usage is always maintained in a reasonable range, the image removed from the screen is usually recycled. The garbage collector will also assume that you no longer have a reference to these pictures to perform GC operations on them. Using this approach to solve the problem is very good, but in order to allow the program to run quickly, in the interface to quickly load the picture, you have to consider that some pictures are recycled, the user then re-slide it into the screen this situation. It is a performance bottleneck to reload the images that have just been loaded, and you need to find a way to avoid the situation.
This time, the use of memory caching technology can be a good solution to this problem, it allows the component to quickly reload and process the picture. Let's take a look at how memory caching technology can be used to cache images so that your application will be able to improve responsiveness and fluency when loading many images.
Memory caching technology provides quick access to images that consume valuable memory from applications. The most core of these classes is LRUCache (this class is provided in the ANDROID-SUPPORT-V4 package). This class is ideal for caching images, and its main algorithm is to store recently used objects in linkedhashmap with strong references, and to remove the least recently used objects from memory before the cached value reaches a predetermined value.
In the past, we often used an implementation of a very popular memory caching technique, either soft or weak (SoftReference or weakreference). However, this is no longer recommended because, starting with Android 2.3 (API Level 9), the garbage collector is more inclined to reclaim objects holding soft or weak references, which makes soft and weak references less reliable. In addition, in Android 3.0 (API level 11), image data is stored in local memory, so it cannot be released in a predictable way, which poses a potential risk of memory overflow and crash of the application.
To be able to choose a suitable cache size for LRUCache, there are several factors that should be taken into account, such as:
- How much memory can your device allocate for each application?
- How many pictures can be displayed on the device screen at a time? How many images need to be preloaded, as it is possible to be displayed on the screen soon?
- What is the screen size and resolution of your device? An ultra-high-resolution device, such as a Galaxy nexus, requires more cache space than a lower-resolution device, such as a Nexus S, when holding the same number of images.
- The size and size of the picture, and how much memory space each image occupies.
- How often are the images accessed? Will there be some images that are more frequently accessed than other images? If so, you might want to have some images reside in memory, or use multiple LRUCache objects to distinguish different groups of pictures.
- Can you maintain a good balance between quantity and quality? In some cases, it is more effective to store multiple low-pixel images, while loads in the background to load high-resolution images.
And not a specified cache size can satisfy all applications, which is up to you. You should analyze the usage of the program's memory and then work out a suitable solution. A cache space that is too small can cause images to be released and reloaded frequently, which does not benefit. A cache space that is too large can still cause java.lang.OutOfMemory exceptions.
Here is an example of using LruCache to cache a picture:
1 PrivateLrucache<string, bitmap>Mmemorycache;2 3 @Override4 protected voidonCreate (Bundle savedinstancestate) {5 //gets the maximum amount of free memory that is used to exceed this value to cause a OutOfMemory exception. 6 //LRUCache passes through the constructor to the cache value, in kilobytes. 7 intMaxMemory = (int) (Runtime.getruntime (). MaxMemory ()/1024);8 //use 1/8 of the maximum available memory value as the size of the cache. 9 intCacheSize = MAXMEMORY/8;TenMmemorycache =NewLrucache<string, bitmap>(cacheSize) { One @Override A protected intsizeOf (String key, Bitmap Bitmap) { - //Override this method to measure the size of each picture, returning the number of pictures by default. - returnBitmap.getbytecount ()/1024; the } - }; - } - + Public voidAddbitmaptomemorycache (String key, Bitmap Bitmap) { - if(Getbitmapfrommemcache (key) = =NULL) { + mmemorycache.put (key, bitmap); A } at } - - PublicBitmap Getbitmapfrommemcache (String key) { - returnMmemorycache.get (key); -}
In this example, one-eighth of the memory allocated by the system to the application is used as the cache size. This will probably have 4 megabytes (32/8) of cache space in the high-profile handsets. A full-screen GridView is populated with 4 800x480 resolution images, which will probably take up 1.5 megabytes of space (800*480*4). Therefore, this cache size can store 2.5 pages of pictures.
When a picture is loaded into the ImageView, it is first checked in the LruCache cache. If the corresponding key value is found, the ImageView is updated immediately, otherwise a background thread is opened to load the image.
1 Public voidLoadBitmap (intresId, ImageView ImageView) {2 FinalString ImageKey =string.valueof (resId);3 FinalBitmap Bitmap =Getbitmapfrommemcache (imagekey);4 if(Bitmap! =NULL) {5 Imageview.setimagebitmap (bitmap);6}Else {7 Imageview.setimageresource (r.drawable.image_placeholder);8Bitmapworkertask task =NewBitmapworkertask (imageView);9 Task.execute (resId);Ten } One}
In this example, one-eighth of the memory allocated by the system to the application is used as the cache size. This will probably have 4 megabytes (32/8) of cache space in the high-profile handsets. A full-screen GridView is populated with 4 800x480 resolution images, which will probably take up 1.5 megabytes of space (800*480*4). Therefore, this cache size can store 2.5 pages of pictures.
When a picture is loaded into the ImageView, it is first checked in the LruCache cache. If the corresponding key value is found, the ImageView is updated immediately, otherwise a background thread is opened to load the image.
1 Public voidLoadBitmap (intresId, ImageView ImageView) {2 FinalString ImageKey =string.valueof (resId);3 FinalBitmap Bitmap =Getbitmapfrommemcache (imagekey);4 if(Bitmap! =NULL) {5 Imageview.setimagebitmap (bitmap);6}Else {7 Imageview.setimageresource (r.drawable.image_placeholder);8Bitmapworkertask task =NewBitmapworkertask (imageView);9 Task.execute (resId);Ten } One}
Bitmapworkertask also puts the key-value pairs of the newly loaded pictures into the cache.
1 classBitmapworkertaskextendsAsynctask<integer, Void, bitmap> {2 //load the picture in the background. 3 @Override4 protectedBitmap doinbackground (Integer ... params) {5 FinalBitmap Bitmap =Decodesampledbitmapfromresource (6Getresources (), params[0], 100, 100);7Addbitmaptomemorycache (String.valueof (params[0]), bitmap);8 returnbitmap;9 }Ten}
Master the above two methods, whether it is to load large images in the program, or to load a large number of pictures, do not worry about oom problem!
Android high-efficiency loading large map, multi-graph solution, effectively avoid the program Oom (turn)