After a long time did not write a blog, now must quickly pulse back. Today I'm still on the last one in a multi-threaded asynchronous load family, the image cache in the ListView is implemented using asynchronous loading and its optimizations. Specifically, this is a comprehensive demo. But personally think that there is a bit of value inside the image of the implementation of the cache. Because honestly, it really can be used in real projects. The main learning comes from the learning of asynchronous loading in the web of Xu Yisheng, inspired by the great God. This is also a summary of the knowledge of the great God and some personal feelings.
This is a comprehensive demo, mainly involved in the knowledge of the main: Network programming, asynchronous loading, JSON parsing, image caching, the use of general ListAdapter. Finally, the effect of a picture-and-text listview that loads the network data is implemented. Of course, this involves more knowledge, but the focus of this time is the image cache and asynchronous loading, of course, similar to the network programming in the Httpurlconnection,json analysis, build a universal adapter and other knowledge will be given in the subsequent blog, here is the use of my own package before, Because in order to simplify development.
This time the focus is on asynchronous loading and picture caching, as for asynchronous loading because it has been written clearly in the first two blogs, this time it is mostly asynchronous loading to see how asynchronous loading is used in the actual project. It is primarily a time-consuming network request with asynchronous loading, and a custom listener is used to callback the data that is obtained as soon as the data is obtained. And then the highlight is the picture cache.
When it comes to picture caching, a cache is recognized in the following ways:
1. Why do I use picture caching?
Very simple "consumption of traffic particularly large", this believe that many people feel the same well, because we may have written a similar Web request data in the ListView of the picture and text of the demo, but if we directly through the network request pictures, and then get the picture displayed on the ListView, When sliding the ListView, the next time it will have slipped over item, it will find that the picture requests a network data again, reload once, that is, swipe to request a network, whether or not to repeat. It is conceivable that the flow of consumption is too large, it is estimated that such a slippery night, the next morning woke up, found that their house has become China Mobile. Another drawback is that every request for a network is an asynchronous and time-consuming process, so you'll notice a lag in sliding the listview.
2. What is the principle of image caching?
Image caching is based on the LRU algorithm to achieve, LRU is least recently used, Chinese meaning is least recently unused algorithm, learned operating system principle know this is the operating system of one of the page replacement algorithm.
Speaking of this, may wish to see how LRUCache source code is introduced.
/** * A Cache that holds strong references to A limited number of values. Each time * A value is accessed, it's moved to the head of a queue. When a value was * added to a full cache, the value at the end of this queue is evicted and could * become eligible for Garba GE Collection. * * <p>if your cached values hold resources this need to be explicitly released, * override {@link #entryRemoved}. * * <p>if A cache miss should is computed on demand for the corresponding keys, * override {@link #create}. This simplifies the calling code, allowing it to * assume a value would always is returned, even when there ' a cache miss. * * <p>by default, the cache size is measured in the number of entries. Override * {@link #sizeOf} to size the cache in different units. For example, this cache * was limited to 4MiB of bitmaps: * <pre> {@code * int cacheSize = 4 * 1024x768 *;//4M IB * lrucache<string, bitmap> bitmapcache = new lrucache<string, bitmap> (cacheSize) {* protected int sizeOf (String key, Bitmap value) {* return value.getbytecount (); *} *}} </pre> * * <p>this class is thread-safe. Perform multiple cache Operations atomically by * Synchronizing on the cache: <pre> {@code * synchronized (cache {* IF (cache.get (key) = = null) {* CACHE.PUT (key, value); *} *}}</pre> * * <p>this clas s does not allow NULL to be used as a key or value. A return * value of NULL from {@link #get}, {@link #put} or {@link #remove} are * unambiguous:the key was ' not ' in the cache . * * <p>this class appeared in Android 3.1 (Honeycomb MR1); It ' s available as part * of <a href= "http://developer.android.com/sdk/compatibility-library.html" >android ' s * Support package</a> for earlier releases. */
LRUCache Main principle: The cache is limited the number of caches, that is, the capacity of the cache is limited, you can imagine a cache of logical memory structure of a queue, when a cache value in the cache is accessed, it will be replaced with the queue's team header, when a cache value needs to be added to the tail, but the queue is full, That is, when the cache space is full, then you need to be in the queue at the end of a cache value out of the queue, that is, to release the queue in the tail part of the cache space, because the LRU algorithm is at the end of the team, it must have been least recently unused. Because the cache space is limited, some data spaces are released in a timely and appropriate manner based on such an algorithm.
The LRUCache class is thread-safe and supports multiple cache operations that are automatically implemented asynchronously, and this class does not allow null values to be used as key or value, and note that the LRUCache key is not stored in the cache.
The Lurcache class appears in the Android3.1 version.
3, LRUCache How to create:
LRUCache actually operates much like a map, a beginner can actually think of it as a map because it is key-value paired and has a put (), and The Get () method is very similar to map
Personally think that using the picture cache usage is very high, for the next convenient use, simply package it into a tool class.
package Com.mikyou.utils;import Android.graphics.bitmap;import Android.util.lrucache;public class LruCacheUtils {// Create a cache cache, the first generic represents the cached identity key, and the second generic represents the object that needs to be cached private lrucache<string, bitmap> mcaches; Public lrucacheutils () {int maxmemory= (int) runtime.getruntime (). MaxMemory ()//Gets maximum application Runtime maximum memory//by obtaining maximum runtime memory, Reasonable allocation of memory space size of the cache int cachesize=maxmemory/4;//1/4;mcaches=new lrucache<string with maximum running memory, bitmap> (cacheSize) {@ overrideprotected int sizeOf (String key, Bitmap value) {//load the correct memory size to return Value.getbytecount ();//To be called every time the cache is saved}}; }//Save picture in LRUCache public void Addbitmaptocache (String url,bitmap Bitmap) {if (Getbitmapfromcache (URL) ==null) {// Determine if the current URL corresponds to the bitmap in the LRU cache, if not in the cache, the current URL corresponding to the bitmap object is added to the LRU cache mcaches.put (URL, bitmap);}} Read the picture from LRUCache public Bitmap getbitmapfromcache (String url) {Bitmap bitmap=mcaches.get (URL);// In fact, LRUCache is a map, the bottom is the return bitmap through HashMap;}}
Through the above knowledge of the explanation, I believe that has a certain understanding of LRUCache, then we will start our demo bar.
1, first, since we are loading network data, so we have to solve the problem of network data source, mainly from the MU lesson network of a course list of the API address, the returned data is JSON format data.
The address is: http://www.imooc.com/api/teacher?type=4&num=60. You can first test the data with a browser, the test results are as follows:
Note: You may see that the Chinese are all garbled here, because Unicode encoding, I will use a tool class in the code to translate these into Chinese.
2, after the data is solved, then is the layout, layout is very simple, the main layout is a Listview,listitem layout is also very simple.
<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http// Schemas.android.com/tools " android:layout_width=" match_parent " android:layout_height=" Match_parent " > <listview android:id= "@+id/listview" android:layout_width= "Match_parent" android: layout_height= "Wrap_content" android:divider= "#22000000" android:dividerheight= "0.2DP" > </ Listview></relativelayout>
<?xml version= "1.0" encoding= "Utf-8"? ><relativelayout xmlns:android= "http://schemas.android.com/apk/res/ Android "Android:layout_width=" Match_parent "android:layout_height=" wrap_content "android:padding=" 5DP "> <imageview android:id= "@+id/c_img" android:layout_width= "140DP" android:layout_height= "90DP" ANDROID:SR c= "@drawable/left_img"/><textview android:id= "@+id/c_name" android:layout_width= "Wrap_content" Android : layout_height= "Wrap_content" android:text= "Android Baidu map Navigation" android:textsize= "16SP" android:layout_marginleft= "5d P "android:layout_torightof=" @id/c_img "android:layout_margintop=" 10DP "/><textview android:id=" @+id/c_l Earner "android:layout_width=" wrap_content "android:layout_height=" wrap_content "android:drawableleft=" @drawable /learner "android:text=" 7897 "android:textcolor=" #a9b7b7 "android:layout_alignbottom=" @id/c_img "android:layou t_alignleft= "@id/c_name" Android:drawablepadding= "5DP" android:layout_marginbottom= "5DP"/></relativelayout>
3, the HttpURLConnection Network request framework in its own package, returns the entire JSON data
Package Com.mikyou.utils;import Java.io.bufferedreader;import Java.io.inputstream;import Java.io.InputStreamReader ; Import Java.net.httpurlconnection;import Java.net.malformedurlexception;import java.net.url;import java.util.List ; Import Org.json.jsonobject;import Android. R.interpolator;public class Mikyouhttpurlconnectionutils {private static stringbuffer buffer;public static String GetData (String urlstring,string apikeyvalue,list<string> stringlist) {buffer=new stringbuffer (); String jsonorxmlstring=null;if (Stringlist!=null) {for (int i = 0; I <stringlist.size (); i++) {URLSTRING+=STRINGLIST.G ET (i);}} try {System.out.println ("URL---->" +urlstring); URL url=new url (urlstring); HttpURLConnection conn= (httpurlconnection) url.openconnection (); Conn.setrequestmethod ("GET"); if (apiKeyValue!= NULL) {Conn.setrequestproperty ("Apikey", Apikeyvalue);} Conn.setconnecttimeout (8000); Conn.setreadtimeout (8000); Conn.connect (); if (Conn.getresponsecode () ==200) { InputStream Is=conn.getinputstream (); BuFferedreader reader=new BufferedReader (New InputStreamReader (IS, "UTF-8") and while ((Jsonorxmlstring=reader.readline ( )!=null) {buffer.append (jsonorxmlstring+ "\ n");} Reader.close (); Is.close ();}} catch (Exception e) {e.printstacktrace ();} String String=unicodeutils.decodeunicode (buffer.tostring ());//Use the Unicodeutils tool class to convert the Unicode encoding to the Chinese return of the UTF-8 display string;}}
4. The JavaBean class object that encapsulates the course object is that each item is an object
Package Com.mikyou.bean;import Java.io.serializable;import Android. R.id;public class Course implements Serializable{private string cname;private string cimgurl; private string Cdescriptor;private string Clearner;public string Getcname () {return cName;} public void Setcname (String cName) {this.cname = CName;} Public String Getcimgurl () {return cimgurl;} public void Setcimgurl (String cimgurl) {this.cimgurl = Cimgurl;} Public String Getcdescriptor () {return cdescriptor;} public void Setcdescriptor (String cdescriptor) {this.cdescriptor = Cdescriptor;} Public String Getclearner () {return clearner;} public void Setclearner (String clearner) {this.clearner = Clearner;}}
5. Sub-class for universal adapter implementation
Package Com.mikyou.adapter;import Java.util.list;import Com.lidroid.xutils.bitmaputils;import Com.mikyou.async.imageloader;import Com.mikyou.bean.course;import Com.mikyou.cache.r;import Com.mikyou.tools.viewholder;import Android.content.context;import Android.widget.imageview;public Class Mylistadapter extends Commonadapter<course>{private imageloader loader;public mylistadapter (Context Context, list<course> Listbeans, int layoutid) {Super (context, Listbeans, layoutid); loader=new imageloader ();} @Overridepublic void Convert (Viewholder holder, Course Course) {holder.settext (R.id.c_name, Course.getcname ()). SetText (R.id.c_learner, Course.getclearner ()); ImageView iv= Holder.getview (r.id.c_img); Iv.settag (Course.getcimgurl ());//First, you need to bind the corresponding URL and the corresponding IV, in order to prevent the picture and the request URL does not correspond to Loader.showimagebyasynctask (iv, Course.getcimgurl ());}}
7, core implementation code:
Package Com.mikyou.async;import Java.io.ioexception;import Java.net.httpurlconnection;import Java.net.malformedurlexception;import Java.net.url;import Java.util.list;import Com.mikyou.bean.Course;import Com.mikyou.utils.lrucacheutils;import Android.graphics.bitmap;import Android.graphics.bitmapfactory;import Android.os.asynctask;import Android.os.handler;import Android.os.message;import Android.util.Log;import Android.util.lrucache;import Android.widget.imageview;public class Imageloader {private ImageView iv;private String Url;private lrucacheutils mcacheutils;public Imageloader () {mcacheutils=new lrucacheutils ();} /** * @author mikyou * Implementation of the main idea: * First, load the picture, first go to LRUCache cache according to the incoming URL as key to fetch the corresponding bitmap object *, if the cache exists corresponding key corresponding value, Then directly remove the key corresponding to the cache bitmap object * and set to ImageView, if not in the cache, then you need to asynchronously load data and picture information in the request network, * Then through the listener's Asyncimglistener callback method to the network request to get the bitmap object, first through the Iv.gettag () * comparison URL if corresponding to the bitmap object set to IV, You also need to add this bitmap object and the corresponding URL in key-value form * to the LRUCache cache by using the Put method. * */public void Showimagebyasynctask (final ImageView iv,final String URL) {//First, read the picture from the cache, if there is a direct use of the cache, if not directly load the network picture bitmap bitmap= Mcacheutils.getbitmapfromcache (URL); LOG.D ("url", url), if (bitmap==null) {//= not in cache, go to the network to download pictures, and remember to put the downloaded picture into the cache Imageasynctask imageasynctask=new Imageasynctask (); Imageasynctask.execute (URL); Imageasynctask.setonimgasynctasklistener (new OnAsyncListener () {@ overridepublic void Asynclistener (list<course> mcourselist) {} @Overridepublic void Asyncimglistener (Bitmap Bitmap) {//Picture request network Data callback method if (Iv.gettag (). Equals (URL)) {//Determine if URL and iv correspond to IV.SETIMAGEBITMAP (bitmap); LOG.D ("Addlru", "Network load and add cache--->" +url); Mcacheutils.addbitmaptocache (URL, bitmap);//Because it is the data requested by the network, so there is no doubt in the cache, So the bitmap object needs to be added to the cache}}}); else{//Otherwise, the Iv.setimagebitmap (Mcacheutils.getbitmapfromcache (URL)) is fetched directly from the cache, or the bitmap object in the cache is read directly log.d ("Getlru", " URL read-out cache---> "+url);}} HttpURLConnection Network request mode to get the network picture input stream, and convert the input flow into a Bitmap object public Bitmap getbitmapfromurl (String url) {Bitmap Bitmap = null;try {URL murl=new url (url); HttpURLConnection conn= (httpurlconnection) murl.openconnection (); bitmap = Bitmapfactory.decodestream (Conn.getinputstream ()); Conn.disconnect ();} catch (Exception e) {e.printstacktrace ();} return bitmap;}}
8, asynchronous load class implementation, there are two main: one is to request the entire network of JSON data, the other is to request to load the network picture, and customize a listener interface.
Package Com.mikyou.async;import Java.util.arraylist;import Java.util.list;import org.json.jsonarray;import Org.json.jsonexception;import Org.json.jsonobject;import Com.mikyou.bean.course;import Com.mikyou.utils.mikyouhttpurlconnectionutils;import Android.os.asynctask;import Android.util.Log;public Class Mikyouasynctask extends Asynctask<string, Void, string>{private list<course> mcourselist;private Onasynclistener listener;//Custom Listener Interface object reference @overrideprotected void OnPreExecute () {mcourselist=new arraylist<course > (); Super.onpreexecute ();} @Overrideprotected string Doinbackground (String ... params) {string Data=mikyouhttpurlconnectionutils.getdata (params [0], NULL, NULL);//network request JSON data return;} @Overrideprotected void OnPostExecute (String result) {//Parse JSON data LOG.D ("info", result); try {jsonobject object=new Jsonobject (result); Jsonarray Array=object.getjsonarray ("data"); for (int i = 0; i < array.length (); i++) {Course mcourse=new Course (); Jsonobject Object2=array.getjsonobjecT (i); Mcourse.setcname (object2.getstring ("name")); Mcourse.setcimgurl (object2.getstring ("Picsmall")); Mcourse.setclearner (Object2.getint ("learner") + ""); Mcourse.setcdescriptor (object2.getstring ("description")); Mcourselist.add (mcourse);} if (listener!=null) {//Determines whether the listener Listener.asynclistener (mcourselist) is registered or not,///through the callback method in the listener, the parsed, encapsulated collection of objects after the asynchronously loaded data is recalled}} catch (Jsonexception e) {e.printstacktrace ();} Super.onpostexecute (result);} public void Setonasynctasklistener (Onasynclistener listener) {//Publish a method for registering listeners This.listener=listener;}}
Imgasynctask Asynchronous Load class:
Package Com.mikyou.async;import Java.net.httpurlconnection;import Java.net.url;import android.graphics.Bitmap; Import Android.graphics.bitmapfactory;import Android.media.image;import Android.os.asynctask;import Android.text.getchars;import Android.util.lrucache;import Android.widget.imageview;public class ImageAsyncTask Extends Asynctask<string, Void, bitmap>{private Onasynclistener listener; @Overrideprotected Bitmap Doinbackground (String ... params) {return Getbitmapfromurl (Params[0]);} @Overrideprotected void OnPostExecute (Bitmap result) {if (listener!=null) {Listener.asyncimglistener (result);} Super.onpostexecute (result);} Public Bitmap getbitmapfromurl (String url) {Bitmap Bitmap = null;try {url murl=new url (url); HttpURLConnection conn= (httpurlconnection) murl.openconnection (); bitmap = Bitmapfactory.decodestream ( Conn.getinputstream ()); Conn.disconnect ();} catch (Exception e) {e.printstacktrace ();} return bitmap;} public void Setonimgasynctasklistener (Onasynclistener listener) {This.liStener=listener;}}
Custom Listener:
Listener interface:
Package Com.mikyou.async;import Java.util.list;import Com.mikyou.bean.course;import android.graphics.bitmap;public Interface Onasynclistener {public void Asynclistener (list<course> mcourselist);p ublic void Asyncimglistener ( Bitmap Bitmap);}
Operation Result:
The results of the run without the image cache will find that whenever the swipe requests the network, the image loading has a delay time:
Running results after adding a picture cache will find that it is very smooth and there is no delay in picture loading when reading cached pictures directly
Finally, the image cache LRUCache in fact very popular, and in many popular network framework, we all know the very popular xutils framework, there is a bitmaputils, it is implemented in the cache principle is based on LRUCache.
Talking about the cache and optimization of images in the asynchronous loading ListView in Android three