In the previous blog post on Android bitmap memory restrictions, we learned in detail and analyzed why Android
The OOM error occurs when bitmap is used. In short, Android uses local code to complete decoding during image decoding, but the memory used is the memory in the heap, the heap memory size is limited by the available memory size of the VM instance. Therefore, when the available memory of the application cannot meet the decoding needs, Android will throw an OOM error.
Here is an aside, that is, why does Android limit the available memory size of each application? In fact, there may be many answers to this question. At present, I have considered two points:
- This makes the memory usage more reasonable and limits the maximum available memory of each application. This can prevent some applications from malicious or unintentional use of excessive memory, leading to the failure of other applications, we all know that android has multiple processes. If a process (that is, an application) consumes too much memory, what about other applications? Of course, there is actually an exception here, that is, if your application uses a lot of local code, creating an object in the local code to decode the image will not be computed, this is because the locally created object or decoded image uses the memory of the local heap, which is the same as that of the system. We call bitmapfactory through the framework. in decodefile () method decoding, although the system also calls local code for decoding, Android
When the framework is implemented, it deliberately allocates the memory used for decoding from the heap instead of the memory allocated from the local heap, of course, it does not mean that OOM will not occur when allocated from the local heap. If the memory allocated by the local heap exceeds the available memory limit of the system, it is usually a direct crash and may not be seen in any errors, there may be some bad bytecode and so on.
- Power-saving considerations, uh ..., The reason does not seem to be clear to me.
Back to the question, we may often encounter the need to display dozens or even hundreds of images on a single interface during application design and development, of course, limited to the size of the mobile phone screen, we usually use a control similar to a list or a grid in the design. That is to say, the number of images is usually a relatively fixed number at a time, it is usually not too big. If the number of images is relatively large, the size of the widget is usually relatively small. In this case, you can use the thumbnail policy. Next, let's take a look at how to avoid OOM errors. For this solution, refer to imagedownloader in XML adapters of the android demonstration program. the implementation in Java mainly uses a second-level cache similar mechanism, that is, there is a bitmap object reference that directly holds the decoded data structure, at the same time, a secondary cache data structure is used to hold the softreference object of the decoded bitmap object. Due to the special nature of the softreference object, the system will first release the object held by the softreference object when the memory is required, that is to say, when the VM finds that the available memory is relatively small and it needs to trigger GC, bitmap in the second-level cache will be recycled first, and the bitmap object in the first-level cache will be used for display.
In fact, the most critical aspect of this solution is the use of a suitable data structure, that is, the linkedhashmap type for the first-level cache bitmap container. Due to the particularity of linkedhashmap, we can control the number of internal storage objects and Remove unused objects from the container, which provides the possibility of second-level cache, we can keep the recently accessed bitmap object in the first-level cache, when the size of the accessed image exceeds our preset value, the objects with the longest time in the container will be removed, at this time, we can store the objects removed from the linkedhashmap to the second-level cache container, and the management of the objects in the second-level cache is done by the system, when GC is required, the system first recycles the bitmap object in the second-level cache container. Search for an object from the level-1 cache container first. If a corresponding object exists, it can be directly returned. If not, search for the corresponding softreference object from the level-2 cache, determine whether the bitmap held by the softreference object is available. If it is available, return directly. Otherwise, return null.
The main code segment is as follows:
Private Static final int hard_cache_capacity = 16;
// Hard cache, with a fixed maximum capacity and a Life Duration
Private Static final hashmap <string, bitmap> shardbitmapcache = new linkedhashmap <string, bitmap> (hard_cache_capacity/2, 0.75f, true ){
Private Static final long serialversionuid =-57721379457331894l;
@ Override
Protected Boolean removeeldestentry (linkedhashmap. Entry <string, bitmap> eldest ){
If (SIZE ()> hard_cache_capacity ){
// Entries push-out of Hard reference cache are transferred to soft reference Cache
Ssoftbitmapcache. Put (eldest. getkey (), new softreference <bitmap> (eldest. getvalue ()));
Return true;
} Else
Return false;
}
};
// Soft cache for bitmap kicked out of hard Cache
Private Final Static concurrenthashmap <string, softreference <bitmap> ssoftbitmapcache = new concurrenthashmap <string, softreference <bitmap> (hard_cache_capacity/2 );
/**
* @ Param ID
* The ID of the image that will be retrieved from the cache.
* @ Return the cached bitmap or null if it was not found.
*/
Public bitmap getbitmap (string ID ){
// First try the Hard reference Cache
Synchronized (shardbitmapcache ){
Final Bitmap bitmap = shardbitmapcache. Get (ID );
If (Bitmap! = NULL ){
// Bitmap found in hard Cache
// Move element to first position, so that it is removed last
Shardbitmapcache. Remove (ID );
Shardbitmapcache. Put (ID, bitmap );
Return bitmap;
}
}
// Then try the soft reference Cache
Softreference <bitmap> bitmapreference = ssoftbitmapcache. Get (ID );
If (bitmapreference! = NULL ){
Final Bitmap bitmap = bitmapreference. Get ();
If (Bitmap! = NULL ){
// Bitmap found in soft Cache
Return bitmap;
} Else {
// Soft reference has been garbage collected
Ssoftbitmapcache. Remove (ID );
}
}
Return NULL;
}
Public void putbitmap (string ID, Bitmap bitmap ){
Synchronized (shardbitmapcache ){
If (shardbitmapcache! = NULL ){
Shardbitmapcache. Put (ID, bitmap );
}
}
}
In the above Code, ID is used to identify a bitmap object. You may choose different methods to index bitmap objects in actual applications, here we will not repeat the decoding of the image. This article mainly discusses how to manage bitmap objects so that OOM errors do not occur easily in actual applications. In fact, in this solution, the value of hard_cache_capacity is an experience value, in addition, this is directly related to the actual size of the image to be decoded in each application. If the image size is too large, the value may have to be reduced. If the image size is small, it can be increased as appropriate. This solution mainly discusses a mechanism for using softreference in combination with dual-cache, by using the second-level cache and the system's collection features of softreference objects, the system automatically recycles bitmap objects of images that are no longer sensitive, while retaining a level-1 cache, that is, sensitive image bitmap objects.