Full parsing of Android asynchronous loading introduced cache
Why do you want to cacheThrough the scaling of the image, we do the large image of the asynchronous loading optimization, but now the app is not only high-definition large image, but also high-definition multi-image, is still the picture and text mixed, to the text, if these pictures are loaded into memory, will be oom. Therefore, after the user browses the image, the discarded images should be collected immediately, however, this brings another problem, that is, when the user after browsing through the picture, if you want to return to re-browse, then the recovered image will be reloaded again, You're going to have to watch you recycle GC while watching you reload. These two things are certainly contradictory, but also affect the performance of a very important reason.
Memory CacheGoogle offers a set of memory caching techniques for a problem that really needs to find a balance point. Memory caching technology provides quick access to images that consume valuable memory from applications. One of the most core classes is LRUCache. 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. LruCache was introduced in Support-v4, and before the introduction of LruCache, Google suggested using soft or weak references (softreference or weakreference) for memory caching. But starting with Android 2.3, the GC algorithm changes, soft references and weak references will also be prioritized by GC recycling, so this method is not too high value, and now many online continue to use SoftReference and WeakReference articles, Most of them are outdated articles, suggest that we keep up with the pace of the party, with the Times.
LRUCache UseThe memory cache size used by the memory cache LRUCache is determined by the developer, and developers need to consider many factors, such as usage, resolution, frequency of access, and device performance. This equilibrium point often requires a lot of experience and testing to decide. Using LRUCache is simple:
Private lrucache<string, bitmap> mmemorycaches;//gets the application memory int maxmemory = (int) runtime.getruntime (). MaxMemory (); /Assign Cacheint cacheSize = maxmemory/10;mmemorycaches = new lrucache<string, bitmap> (cacheSize) { @Override
protected int sizeOf (String key, Bitmap value) { return value.getbytecount (); } };/ /from LRUCache Gets the cache object public Bitmap getbitmapfrommemorycaches (String url) { return mmemorycaches.get (URL);} Increase the cache object to Lrucachepublic void Addbitmaptomemorycaches (String url,bitmap Bitmap) { if (getbitmapfrommemorycaches ( URL) = = null) { mmemorycaches.put (URL, bitmap);} }
First, we need to declare LRUCache, and then, through the LRUCache construction method to create the cache object and assign it cachesize, this cachesize usually we need to get through the runtime to get the current system to distribute the memory available to the app, and use a portion of these memory as LRUCache caches. The sizeof method must be overridden in LRUCache, in which case LRUCache can get the size of each cache object, and subclasses must override it because the default LRUCache gets the number of caches ... Nima.Finally, we provide two methods getbitmapfrommemorycaches and addbitmaptomemorycaches are used respectively to obtain and increase the memory cache to LRUCache.Wait, we seem to have not written the method of releasing memory, yes, you do not have to write, the LRU algorithm can guarantee that CacheSize will not oom, once the size is exceeded, the GC will reclaim the longest object, free space.
Join a first-level cache for asynchronous processingOK, after learning the basics of caching, let's go back to this example and think about how to optimize for asynchronous processing using caching. First of all, the ListView, the GridView these spoiled things, can not fall, but also not in it roll happy time, you still in the back desperately play load. So, the first point, roll the time to let it happy to roll, and then start loading.
Roll over and ReloadTo achieve this, we can do this by adding Abslistview.onscrolllistener interfaces to adapter.Of course, there is a point to note, the first time initialization, it is necessary to manually load the picture, or the system will judge you not to roll, can only call the Onscroll method, do not call the Onscrollstatechanged method. And we also need to keep getting the visible item in the Onscroll method. It is important to note that VisibleItemCount, as long as it is greater than 0, is considered to be starting to display the picture.
@Overridepublic void onscrollstatechanged (abslistview view, int scrollstate) { if (scrollstate = = Scroll_state_idle ) { mimageloader.loadimages (Mstart, mEnd); } else { mimageloader.cancelalltasks (); }} @Overridepublic void Onscroll (abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) { Mstart = Firstvisibleitem; MEnd = Firstvisibleitem + visibleitemcount; if (mfirstflag && visibleitemcount > 0) { mimageloader.loadimages (Mstart, mEnd); Mfirstflag = false; }}
Load the displayed itemsWhen loading the data, get the first item that can be displayed and the last visible item, just load this part. So we create a method--loadimages (int start, int end). This method is used to load the item data from start to end.Load, the first from the memory cache to fetch, if there is, that the recent load has been loaded, that the direct loading is good, if not taken, then open synctask to download.
public void loadimages (int start, int end) {for (int i = start; i < end; i++) { String url = images.image_urls [i]; Bitmap Bitmap = getbitmapfrommemorycaches (URL); if (bitmap = = null) { asyncdownloadimage task = new asyncdownloadimage (URL); Mtasks.add (Task); Task.execute (URL); } else { ImageView ImageView = (ImageView) mlistview.findviewwithtag (URL); Imageview.setimagebitmap (bitmap);}}}
Here we set the picture, directly through the Findviewwithtag, through the URL to find the corresponding ImageView, here is different from the previous because we are here to start to end to load, It is easier to get the corresponding ImageView directly from the ListView object.
Download and AsynctaskThe download is still using the old method:
private static Bitmap Getbitmapfromurl (String urlstring) { Bitmap Bitmap; InputStream is = null; try { url url = new URL (urlstring); HttpURLConnection conn = (httpurlconnection) url.openconnection (); is = new Bufferedinputstream (Conn.getinputstream ()); Bitmap = Bitmapfactory.decodestream (is); Conn.disconnect (); return bitmap; } catch (Exception e) { e.printstacktrace (); } finally { try { if (is! = null) is.close (); } catch (IOException e) { } } return null;}
Asynctask is basically similar to the previous:
Class Asyncdownloadimage extends Asynctask<string, Void, bitmap> { private String URL; Public asyncdownloadimage (String URL) { this.url = URL; } @Override protected Bitmap doinbackground (String ... params) { URL = params[0]; Bitmap Bitmap = getbitmapfromurl (URL); if (bitmap! = null) { addbitmaptomemorycaches (URL, bitmap); } return bitmap; } @Override protected void OnPostExecute (Bitmap Bitmap) { super.onpostexecute (Bitmap); ImageView ImageView = (ImageView) mlistview.findviewwithtag (URL); if (ImageView! = null && bitmap! = null) { imageview.setimagebitmap (bitmap); } Mtasks.remove (this);} }
The only difference is that after we download the image, we load the image into the LRUCache.
AssemblyOK, everything is available, ready to brush the code. Before the brush, we first to re-organize the idea, first, in the adapter, a load listview, began to download the display range of item's image, when the cache is of course not, so have to download, the end is displayed in the item, and cached, if not finished, You can't wait to roll up, then immediately cancel all the task, let the ListView roll happily, after rolling, continue to load.OK, the talk is said, the following we began to brush the code, everything in the words, only the code most understand you.
Package Com.imooc.listviewacyncloader;import Android.graphics.bitmap;import Android.graphics.bitmapfactory;import Android.os.asynctask;import Android.util.lrucache;import Android.widget.imageview;import Android.widget.ListView; Import Java.io.bufferedinputstream;import Java.io.ioexception;import Java.io.inputstream;import Java.net.httpurlconnection;import Java.net.url;import Java.util.hashset;import Java.util.Set;public class imageloaderwithcaches {private set<asyncdownloadimage> mtasks; Private lrucache<string, bitmap> mmemorycaches; Private ListView Mlistview; Public Imageloaderwithcaches (ListView listview) {This.mlistview = ListView; Mtasks = new hashset<> (); int maxmemory = (int) runtime.getruntime (). MaxMemory (); int cacheSize = MAXMEMORY/10; Mmemorycaches = new lrucache<string, bitmap> (cacheSize) {@Override protected int sizeOf (Strin G key, Bitmap value) {return value.geTbytecount (); } }; } public void ShowImage (String url, ImageView ImageView) {Bitmap Bitmap = getbitmapfrommemorycaches (URL); if (bitmap = = null) {Imageview.setimageresource (r.drawable.ic_launcher); } else {imageview.setimagebitmap (bitmap); }} public Bitmap getbitmapfrommemorycaches (String URL) {return mmemorycaches.get (URL); public void Addbitmaptomemorycaches (String url,bitmap Bitmap) {if (getbitmapfrommemorycaches (URL) = = null) { Mmemorycaches.put (URL, bitmap); }} public void Loadimages (int start, int end) {for (int i = start; i < end; i++) {String URL = Images.image_urls[i]; Bitmap Bitmap = getbitmapfrommemorycaches (URL); if (bitmap = = null) {asyncdownloadimage task = new asyncdownloadimage (URL); Mtasks.add (Task); Task.execute (URL); } else { ImageView ImageView = (ImageView) mlistview.findviewwithtag (URL); Imageview.setimagebitmap (bitmap); }}} private static Bitmap Getbitmapfromurl (String urlstring) {Bitmap Bitmap; InputStream is = null; try {URL url = new URL (urlstring); HttpURLConnection conn = (httpurlconnection) url.openconnection (); is = new Bufferedinputstream (Conn.getinputstream ()); Bitmap = Bitmapfactory.decodestream (IS); Conn.disconnect (); return bitmap; } catch (Exception e) {e.printstacktrace (); } finally {try {if (is! = null) is.close (); } catch (IOException e) {}} return null; public void Cancelalltasks () {if (mtasks! = null) {for (Asyncdownloadimage task:mtasks) { Task.cancel (FALSE); }}} class AsynCdownloadimage extends Asynctask<string, Void, bitmap> {private String URL; Public asyncdownloadimage (String url) {this.url = URL; } @Override protected Bitmap doinbackground (String ... params) {URL = params[0]; Bitmap Bitmap = getbitmapfromurl (URL); if (bitmap! = null) {addbitmaptomemorycaches (URL, bitmap); } return bitmap; } @Override protected void OnPostExecute (Bitmap Bitmap) {super.onpostexecute (BITMAP); ImageView ImageView = (ImageView) mlistview.findviewwithtag (URL); if (ImageView! = null && bitmap! = null) {Imageview.setimagebitmap (bitmap); } mtasks.remove (this); } }}
Here is the code for adapter:
Package Com.imooc.listviewacyncloader;import Android.content.context;import Android.view.layoutinflater;import Android.view.view;import Android.view.viewgroup;import Android.widget.abslistview;import Android.widget.baseadapter;import Android.widget.imageview;import Android.widget.listview;import java.util.List; public class Myadapterusecaches extends Baseadapter implements Abslistview.onscrolllistener {private LAYOUTINFL Ater Minflater; Private list<string> Mdata; Private Imageloaderwithcaches Mimageloader; private int mstart = 0, mEnd = 0; Private Boolean Mfirstflag; Public Myadapterusecaches (context context, list<string> data, ListView listview) {this.mdata = data; Minflater = Layoutinflater.from (context); Mimageloader = new Imageloaderwithcaches (ListView); Mimageloader.loadimages (Mstart, mEnd); Mfirstflag = true; Listview.setonscrolllistener (this); } @Override public int getcount () {return Mdata.size (); } @Override public Object getItem (int position) {return mdata.get (position); } @Override public long getitemid (int position) {return position; } @Override public View getView (int position, view Convertview, ViewGroup parent) {String URL = mdata.get (PO Sition); Viewholder viewholder = null; if (Convertview = = null) {Viewholder = new Viewholder (); Convertview = minflater.inflate (R.layout.listview_item, NULL); Viewholder.imageview = (ImageView) Convertview.findviewbyid (R.id.iv_lv_item); Convertview.settag (Viewholder); } else {Viewholder = (Viewholder) convertview.gettag (); } viewHolder.imageView.setTag (URL); ViewHolder.imageView.setImageResource (R.drawable.ic_launcher); Mimageloader.showimage (URL, viewholder.imageview); return convertview; } @Override public void onscrollstatechanged (ABslistview view, int scrollstate) {if (scrollstate = = Scroll_state_idle) {mimageloader.loadimages (Msta RT, MEnd); } else {mimageloader.cancelalltasks (); }} @Override public void onscroll (Abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcou NT) {mstart = Firstvisibleitem; MEnd = Firstvisibleitem + visibleitemcount; if (mfirstflag && visibleitemcount > 0) {mimageloader.loadimages (Mstart, mEnd); Mfirstflag = false; }} public class Viewholder {public ImageView ImageView; }}
is not very simple, now the introduction of the cache, downloaded pictures will be temporarily saved in memory, MOM no longer have to worry about you oom.We try to pull down, the downloaded picture reappears or it can be loaded immediately, unless the slide is too much to cause the GC.
As you can see, our use of caching for loading has several features:1. Load when initializing2. Load only when sliding3. Loading content in the staging cache4. Load only the displayed area
We will continue to optimize the cache after the ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ my github
My video mu-class network
Full parsing of Android asynchronous loading introduced first-level cache