Original address: http://developer.android.com/training/displaying-bitmaps/manage-memory.html
In addition to the steps described in the previous section, there are some details that can facilitate recycling of the garbage collector and reuse of bitmaps. The recommended strategy depends on the target version of Android. The sample app Bitmapfun shows how to make your application work efficiently on different versions.
To lay the groundwork for this lesson, there are some improvements to how the Android system manages bitmap memory to understand:
- Before Android 2.2, the thread of the app was stopped when the garbage collector was recycled. This can degrade performance latency. Android 2.3 Adds the ability to collect garbage concurrently, which means that the bitmap cannot be referenced again shortly after the memory is reclaimed.
- Prior to Android 2.3.3, the support data for the bitmap was stored in local memory. This is separate from the bitmap itself and is stored in the Dalvik stack. Pixel data that is expected to be in local memory will not be freed and may cause the program to exceed its own memory limit and then crash. starting with Android 3.0, the bitmap associated with the pixel data is stored together in the Dalvik virtual machine stack.
The following sections describe how to optimize and manage bitmap memory for different versions of Android.
Managing memory on versions prior to Android 2.3
It is recommended to use the Recycle () method before 2.3. If you show a lot of bitmap data in the app, you're likely to encounter outofmemoryerror errors. The Recycle () method allows the application to reclaim memory as much as possible.
Caution: You should use the Recycle () method when making sure the bitmap is no longer in use. If you call the Recycle () method and try to draw the bitmap, you will get an error: "Canvas:trying to use a recycled bitmap".
The following code is a sample usage of the Recycle () method. It uses a reference count to track whether a bitmap is currently in the state of being displayed or cached. The code reclaims the bitmap when the following conditions are in:
- The number of references Mdisplayrefcount and Mcacherefcount are 0.
- The bitmap is not null and has not been reclaimed.
Private intMcacherefcount =0;Private intMdisplayrefcount =0;...//Notify The drawable , the displayed state has changed.//Keep a count to determine when the drawable is no longer displayed. Public void setisdisplayed(Booleanisdisplayed) {synchronized( This) {if(isdisplayed) {mdisplayrefcount++; mhasbeendisplayed =true; }Else{mdisplayrefcount--; } }//Check to see if recycle () can is called.CheckState ();}//Notify The drawable , the cache state has changed.//Keep a count to determine when the drawable is no longer being cached. Public void setiscached(Booleaniscached) {synchronized( This) {if(iscached) {mcacherefcount++; }Else{mcacherefcount--; } }//Check to see if recycle () can is called.CheckState ();}Private synchronized void CheckState() {//If the drawable cache and display ref counts = 0, and this drawable //have been displayed, then recycle. if(Mcacherefcount <=0&& Mdisplayrefcount <=0&& mhasbeendisplayed && Hasvalidbitmap ()) {Getbitmap (). Recycle (); }}Private synchronized Boolean Hasvalidbitmap() {Bitmap Bitmap = Getbitmap ();returnBitmap! =NULL&&!bitmap.isrecycled ();}
Managing memory on versions after Android 3.0
The BitmapFactory.Options.inBitmap property is described in Android 3.0. If this option is set, then the decoding method attempts to reuse an existing bitmap. This means that the bitmap's memory is reusable, which can lead to improved performance and no need to request memory and free memory. However, there are some restrictions on how to use Inbitmap, which only supports bitmaps of the same size before Android 4.4. For more information, see the Documentation: Inbitmap.
Save the bitmap for later use
The following code shows how to store a bitmap for later use. When the app is running above Android 3.0, the bitmap is removed by LRUCache, and a soft reference to a reference bitmap is placed in a hashset so that it can be used later:
Set<softreference<bitmap>> Mreusablebitmaps;PrivateLrucache<string, bitmapdrawable> Mmemorycache;//If you ' re running on honeycomb or newer, create a//Synchronized HashSet of references to reusable bitmaps.if(Utils.hashoneycomb ()) {mreusablebitmaps = Collections.synchronizedset (NewHashset<softreference<bitmap>> ());} Mmemorycache =NewLrucache<string, bitmapdrawable> (mcacheparams.memcachesize) {//Notify The removed entry is no longer being cached. @Override protected void entryremoved(BooleanEvicted, String key, Bitmapdrawable OldValue, bitmapdrawable newvalue) {if(RecyclingBitmapDrawable.class.isInstance (OldValue)) {//The removed entry is a recycling drawable, so notify it //That it had been removed from the memory cache.((recyclingbitmapdrawable) oldValue). setiscached (false); }Else{//The removed entry is a standard bitmapdrawable. if(Utils.hashoneycomb ()) {//We ' re running on honeycomb or later, so add the bitmap //To a SoftReference set for possible use with Inbitmap later.Mreusablebitmaps.add (NewSoftreference<bitmap> (Oldvalue.getbitmap ())); } } }....}
Use a bitmap that already exists
Within the running app, the decoding method checks to see if any existing bitmaps can be reused:
publicstaticdecodeSampledBitmapFromFile(String filename, intint reqHeight, ImageCache cache) { finalnew BitmapFactory.Options(); ... BitmapFactory.decodeFile(filename, options); ... // If we‘re running on Honeycomb or newer, try to use inBitmap. if (Utils.hasHoneycomb()) { addInBitmapOptions(options, cache); } ... return BitmapFactory.decodeFile(filename, options);}
Addinbitmapoptions ():
Private Static void addinbitmapoptions(Bitmapfactory.options Options, Imagecache Cache) {//Inbitmap only works with mutable bitmaps, so force the decoder to //Return mutable bitmaps.Options.inmutable =true;if(Cache! =NULL) {//Try to find a bitmap to use for Inbitmap.Bitmap inbitmap = cache.getbitmapfromreusableset (options);if(Inbitmap! =NULL) {//If a suitable bitmap has been found, set it as the value of //Inbitmap.Options.inbitmap = Inbitmap; } }}//This method iterates through the reusable bitmaps, looking for one// to use for Inbitmap:protectedBitmapGetbitmapfromreusableset(Bitmapfactory.options Options) {Bitmap Bitmap =NULL;if(Mreusablebitmaps! =NULL&&!mreusablebitmaps.isempty ()) {synchronized(Mreusablebitmaps) {Finaliterator<softreference<bitmap>> Iterator = Mreusablebitmaps.iterator (); BITMAP item; while(Iterator.hasnext ()) {item = Iterator.next (). get ();if(NULL! = Item && item.ismutable ()) {//Check to see it the item can is used for inbitmap. if(Canuseforinbitmap (item, options)) {bitmap = Item;//REMOVE from reusable set so it can ' t be used again.Iterator.remove (); Break; } }Else{//Remove from the set if the reference have been cleared.Iterator.remove (); } } } }returnBitmap;}
Finally, the method here checks whether the candidate bitmap satisfies the size:
Static BooleanCanuseforinbitmap (Bitmap candidate, bitmapfactory.options targetoptions) {if(Build.VERSION.SDK_INT >= build.version_codes. KITKAT) {//From Android 4.4 (KitKat) onward we can re-use if the byte size of //The new bitmap is smaller than the reusable bitmap candidate //Allocation byte count. intwidth = targetoptions.outwidth/targetoptions.insamplesize;intHeight = targetoptions.outheight/targetoptions.insamplesize;intByteCount = width * Height * getbytesperpixel (candidate.getconfig ());returnByteCount <= Candidate.getallocationbytecount (); }//On earlier versions, the dimensions must match exactly and the insamplesize must be 1 returnCandidate.getwidth () = = Targetoptions.outwidth && candidate.getheight () = = Targetoptions.outheight && Targetoptions.insamplesize = =1;}/** * A helper function to return the byte usage per pixel of A bitmap based in its configuration. * /Static intGetbytesperpixel (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;}
The method shown in the following fragment is called by the fragment above. It looks for the bitmap that exists and then sets it to a value. Note that this method only sets the value for the appropriate matching object.
PS: In addition to everyone's memory of the image, you may also be on the image of the cache, asynchronous loading, loading large map is also more concerned.
Android Official Development Document Training Series Chinese version: Manage bitmap memory efficiently with bitmap display