Explanation of the Bitmap objects you don't know about Android, androidbitmap
Reprinted please indicate this article from xiaanming blog (http://blog.csdn.net/xiaanming/article/details/41084843), please respect others' hard work results, thank you!
We know that the memory allocated to each application by the Android system is limited. Bitmap, as a large memory-consuming user, may cause OutOfMemoryError if we manage Bitmap improperly, the Bitmap object has some differences in different Android versions. Today we will introduce these differences and provide some notes for using Bitmap.
In Android2.3.3 (API 10) and earlier versions, Bitmap objects are stored separately from their pixel data, and Bitmap objects are stored in Dalvik heap, the pixel data of Bitmap objects is stored in Native Memory (local Memory) or Derict Memory (Direct Memory, this makes the release of pixel data stored in Native Memory unpredictable. We can call the recycle () method to release the pixel data in Native Memory, the premise is that you can clearly confirm that Bitmap is no longer used. If you call the Bitmap object recycle () and then draw the Bitmap, "Canvas: trying to use a recycled bitmap "error, but after Android3.0 (API 11), the Bitmap pixel data and Bitmap object are stored in Dalvik heap together, so we do not need to manually call recycl E () to release the Bitmap object. The memory is released by the garbage collector. Maybe you will ask, why do I still encounter an OutOfMemoryError when displaying the Bitmap object?
Before talking about this problem, I would like to mention that before Android2.2 (API 8), I used the Serial garbage collector. From the name, we can see that this is a single-thread collector, here, the "single thread" means not only using a CPU or a collection thread to collect garbage, but more importantly, when it collects garbage, it must suspend all other working threads, after Android2.3, this kind of collector is replaced by the concurrent garbage collector, which means that our garbage collection thread and our working thread do not affect each other.
After a brief understanding of the garbage collector, let's take a simple example of the above problem. If the system starts the garbage collection thread to collect garbage, then we will generate a large number of Bitmap objects at once, in this case, an OutOfMemoryError may occur, because the Garbage Collector must first determine whether an object is still alive (JAVA determines whether the object is alive using the Root search algorithm GC Root Tracing ), the garbage collection algorithm is used to collect garbage. Different garbage collectors have different garbage collection algorithms, which all take time. When an OutOfMemoryError occurs, we need to clarify whether Memory overflow is caused by Memory leakage. If Memory leakage occurs, we need to use tools (such as MAT) find out the memory leakage code and correct it. If there is no leakage, in other words, the object in the memory must still be alive. Then we can see if it can be done in some way, reduce the memory consumption of objects. For example, when using Bitmap BitmapFactory is used based on The View Size. options calculates the appropriate inSimpleSize to crop Bitmap to reduce the memory usage of Bitmap. If the preceding settings are complete, there is still an OutOfMemoryError (which rarely happens, we can only increase the size of Dalvik heap. In Android 3.1 and later versions, we can use AndroidManifest. add an android: largeHeap attribute with a value equal to "true" to the xml application tag to notify Dalvik Virtual Machine Applications to use a large Java Heap, but we do not encourage this.
Manage Bitmap on Android 2.3 and below
We know from the above that we recommend using the recycle () method to release the memory in Android2.3 and below. When should we call recycle () when using ListView or GridView? Here we use the reference count, and use a variable (dispalyRefCount) to record the Bitmap display. If the Bitmap is drawn on the View, displayRefCount plus one, otherwise one is subtracted, only when displayResCount is 0 and Bitmap is not empty and Bitmap has not called recycle (), we need to recycle () the Bitmap object (), so we need to use a class to wrap the Bitmap object. The Code is as follows:
Package com. example. bitmap; import android. content. res. resources; import android. graphics. bitmap; import android. graphics. drawable. bitmapDrawable; public class RecycleBitmapDrawable extends BitmapDrawable {private int displayResCount = 0; private boolean mHasBeenDisplayed; public RecycleBitmapDrawable (Resources res, Bitmap) {super (res, bitmap );} /*** @ param isDisplay */public void setIsDisplay Ed (boolean isDisplay) {synchronized (this) {if (isDisplay) {mHasBeenDisplayed = true; displayResCount ++;} else {displayResCount -- ;}} checkState ();} /*** check the status of the image and determine whether to call recycle */private synchronized void checkState () {if (displayResCount <= 0 & mHasBeenDisplayed & hasValidBitmap ()) {getBitmap (). recycle () ;}}/*** determine whether Bitmap is empty and whether recycle () * @ return */private synchronized boolean hasValidBitmap has been called () {Bitmap bitmap = getBitmap (); return bitmap! = Null &&! Bitmap. isRecycled ();}}
In addition to the above RecycleBitmapDrawable, we also need a custom ImageView to control when to display Bitmap and when to hide Bitmap objects.
Package com. example. bitmap; import android. content. context; import android. graphics. drawable. drawable; import android. graphics. drawable. layerDrawable; import android. util. attributeSet; import android. widget. imageView; public class RecycleImageView extends ImageView {public RecycleImageView (Context context) {super (context);} public RecycleImageView (Context context, AttributeSet attrs) {super (context, attrs );} public RecycleImageView (Context context, AttributeSet attrs, int defStyle) {super (context, attrs, defStyle) ;}@ Overridepublic void Merge (Drawable drawable) {Drawable vertex = getDrawable (); super. setImageDrawable (drawable); // display the new drawing (drawable, true); // reclaim the previous picture policydrawable (previousDrawable, false);} @ Overrideprotected void onDetachedFromWindow () {// when the View is detached from the window, clear drawablesetImageDrawable (null); super. onDetachedFromWindow ();}/*** notifies the drawable to display or hide the drawable ** @ param Drawable * @ param isDisplayed */public static void policydrawable (drawable, boolean isDisplayed) {if (drawable instanceof RecycleBitmapDrawable) {(RecycleBitmapDrawable) drawable ). setIsDisplayed (isDisplayed);} else if (drawable instanceof LayerDrawable) {LayerDrawable layerDrawable = (LayerDrawable) drawable; for (int I = 0, z = layerDrawable. getNumberOfLayers (); I <z; I ++) {policydrawable (layerDrawable. getDrawable (I), isDisplayed );}}}}
This custom class is also relatively simple, and the setImageDrawable () method is rewritten. In this method, we first obtain the image above ImageView, and then the Drawable shown in ImageView before the notification is not displayed, drawable will determine whether to call recycle (). When the View is detached from the Window, it will call back onDetachedFromWindow (). In this method, we will recycle the image displayed in ImageView. The specific usage method
ImageView imageView = new ImageView(context);imageView.setImageDrawable(new RecycleBitmapDrawable(context.getResource(), bitmap));
You only need to use RecycleBitmapDrawable to wrap the Bitmap object and set it to the ImageView. We don't need to worry about the specific memory release. Is it very convenient? This is the memory for managing Bitmap in Android2.3 and later versions.
Manage Bitmap on Android 3.0 and later
Because Bitmap pixel data is also stored in Dalvik heap in Android3.0 and later versions, the memory management is directly handed over to the garbage collector, and we do not need to manually release the memory, today we are talking about BitmapFactory. options. if this field of inBitmap is set, when decoding Bitmap, it will reuse the Bitmap set by inBitmap to reduce memory allocation and release, this improves the application performance. However, before Android 4.4, BitmapFactory. options. the Bitmap set by inBitmap must be the same as the Bitmap size to be decoded. BitmapFactory after Android4.4. options. the Bitmap set by inBitmap is greater than or equal to the size of the Bitmap to be decoded. Let's assume that a scenario is still using ListView and GridView to load a large number of images. In order to improve For application efficiency, we usually perform the corresponding memory cache and hard disk cache. Here we only talk about the memory cache, and the memory cache officially recommends using LruCache. Note that LruCache only serves to cache data, memory is not recycled. Generally, our code is written like this.
Package com. example. bitmap; import java. lang. ref. softReference; import java. util. collections; import java. util. hashSet; import java. util. iterator; import java. util. set; import android. annotation. targetApi; import android. graphics. bitmap; import android. graphics. bitmap. config; import android. graphics. bitmapFactory; import android. graphics. drawable. bitmapDrawable; import android. OS. build; import android. OS. build. VERSION_CODES; import android. support. v4.util. lruCache; public class ImageCache {private final static int MAX_MEMORY = 4*102*1024; private LruCache <String, BitmapDrawable> mMemoryCache; private Set <SoftReference <Bitmap> mReusableBitmaps; private void init () {if (hasHoneycomb () {mReusableBitmaps = Collections. synchronizedSet (new HashSet <SoftReference <Bitmap> ();} mMemoryCache = new LruCache <Strin G, BitmapDrawable> (MAX_MEMORY) {/*** callback method when the saved BitmapDrawable object is removed from LruCache */@ Overrideprotected void entryRemoved (boolean evicted, String key, bitmapDrawable oldValue, BitmapDrawable newValue) {if (hasHoneycomb () {mReusableBitmaps. add (new SoftReference <Bitmap> (oldValue. getBitmap () ;}}};}/*** obtain the settings that meet the requirements from mReusableBitmaps to BitmapFactory. options. the Bitmap object above inBitmap * @ param options * @ return */p Rotected Bitmap getBitmapFromReusableSet (BitmapFactory. Options options) {Bitmap bitmap = null; if (mReusableBitmaps! = Null &&! MReusableBitmaps. isEmpty () {synchronized (mReusableBitmaps) {final Iterator <SoftReference <Bitmap> iterator = mReusableBitmaps. iterator (); Bitmap item; while (iterator. hasNext () {item = iterator. next (). get (); if (null! = Item & item. isMutable () {if (canUseForInBitmap (item, options) {bitmap = item; iterator. remove (); break;} else {iterator. remove () ;}}} return bitmap;}/*** determine whether the Bitmap can be set to BitmapFactory. options. on inBitmap, ** @ param candidate * @ param targetOptions * @ return */@ TargetApi (VERSION_CODES.KITKAT) public static boolean canUseForInBitmap (Bitmap candidate, BitmapFactory. options targetOptions) {// After Anroid4.4, if you want to use inBitmap, you only need to decode Bitmap smaller than that set in inBitmap. inSampleSize // is not limited to if (Build. VERSION. SDK_INT> = Build. VERSION_CODES.KITKAT) {int width = targetOptions. outWidth/targetOptions. inSampleSize; int height = targetOptions. outHeight/targetOptions. inSampleSize; int byteCount = width * height * getBytesPerPixel (candidate. getConfig (); return byteCount <= candidate. getAllocationByteCount () ;}// before Android // 4.4, if you want to use inBitmap, the decoded Bitmap must be equal to the width and height set by inBitmap, and inSampleSize is 1 return candidate. getWidth () = targetOptions. outWidth & candidate. getHeight () = targetOptions. outHeight & targetOptions. inSampleSize = 1;}/*** get the number of bytes occupied by each pixel ** @ param config * @ return */public static int getBytesPerPixel (Config config) {if (config = Config. ARGB_8888) {return 4;} else if (config = Config. RGB_565) {return 2;} else if (config = Config. ARGB_4444) {return 2;} else if (config = Config. ALPHA_8) {return 1 ;}return 1 ;}@ TargetApi (VERSION_CODES.HONEYCOMB) public static boolean hasHoneycomb () {return Build. VERSION. SDK_INT> = Build. VERSION_CODES.HONEYCOMB ;}}
The above is just some case-based code. Store the weak reference of the BitmapDrawable object removed from LruCache in a set, and then obtain BitmapFactory from the set. options. the Bitmap object of the inBitmap condition is used to improve Bitmap decoding performance.
public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); ... BitmapFactory.decodeFile(filename, options); ... // If we're running on Honeycomb or newer, try to use inBitmap. if (ImageCache.hasHoneycomb()) { options.inMutable = true; if (cache != null) { Bitmap inBitmap = cache.getBitmapFromReusableSet(options); if (inBitmap != null) { options.inBitmap = inBitmap; } } } ... return BitmapFactory.decodeFile(filename, options);}
Through this article, do you have a better understanding of Bitmap objects? If you load a large number of Bitmap objects in an application, I believe that the probability of an OutOfMemoryError in an application is very small, in addition, the performance will be improved to some extent. I often see some students determine whether OutOfMemoryError occurs during their usage when evaluating whether an image loading framework is good or not, of course, frequent OutOfMemoryError occurs. You should first check whether your code has any problems. Generally, some mature frameworks do not have serious problems, after all, it has been well known after many tests. Today's explanation is here. If you have any questions, please leave a message below!