I. Descriptive narrative of the problem |
Android applications often involve loading a large number of images from the network, to increase load speed and efficiency, and to reduce network traffic using level two caching and asynchronous loading mechanisms. The so-called level two cache is obtained from the memory first, then from the file, and then the network. The memory cache (level one) is essentially a map collection that stores the URL and bitmap information of the picture in a key-value-pair manner. Management is relatively complex because memory caches cause heap memory leaks. Third-party components can be used, for experienced can write their own components, and the file cache is relatively simple, usually self-encapsulation.
The following is a case study of how to achieve network image loading optimization.
Second, Case introduction |
List picture of case News
Third, the main core components |
Let's take a look at the components written to implement the first level cache (memory), level two cache (disk file)
1, MemoryCache
The image is stored in memory (first-level cache), using 1 maps to cache the image code such as the following:
public class MemoryCache {//maximum number of caches private static final int max_cache_capacity = 30; Using the map soft reference Bitmap object, ensure that the memory space is not garbage collected private hashmap<string, softreference<bitmap>> Mcachemap = New linkedhashmap<string, softreference<bitmap>> () {private static final long serialversion UID = 1l;//When the number of caches exceeds the specified size (returns TRUE) clears the protected Boolean removeeldestentry that was first placed in the cache (Map.entry<string,softrefere Nce<bitmap>> eldest) {return size () > max_cache_capacity;}; }; /** * Take picture from cache * @param ID * @return Assume that the cache has, and that the picture is not released, return the picture, otherwise return null */public BITMAP get (String ID) { if (!mcachemap.containskey (ID)) return null; Softreference<bitmap> ref = Mcachemap.get (ID); return Ref.get (); }/** * Add picture to cache * @param ID * @param bitmap */public void put (String ID, bitmap bitmap) { Mcachemap.put (ID, new softreference<bitmap> (BITMAP)); }/** * Clears all caches */public void Clear () {try {for (MAP.ENTRY<STRING,SOFTREFERENCE<BITMAP&G T;>entry:mcachemap.entryset ()) {softreference<bitmap> sr = Entry.getvalue (); if (null! = SR) {Bitmap BMP = Sr.get (); if (null! = BMP) Bmp.recycle (); }} mcachemap.clear (); } catch (Exception e) {e.printstacktrace ();} }}
2, Filecache
Cache pictures on disk (Level two cache), code such as the following
public class Filecache {//Cache Files folder private file Mcachedir; /** * Create cache Files folder, fake with SD card. Then use SD, assuming no system comes with cache folder * @param context * @param cachedir Picture cache folder */public Filecache (context context, File Cachedir, String dir) {if (Android.os.Environment.getExternalStorageState (). Equals, (android.os.Environment.MEDIA_ Mounted)) Mcachedir = new File (Cachedir, dir); else Mcachedir = Context.getcachedir ();//How to obtain the system's built-in cache storage path if (!mcachedir.exists ()) mcachedir.mkdirs (); } Public file GetFile (String url) {file f=null; try {//Edit URL to solve Chinese path problem String filename = urlencoder.encode (URL, "Utf-8"); f = new File (mcachedir, filename); } catch (Unsupportedencodingexception e) {e.printstacktrace (); } return F; public void Clear () {///Clear Cache file file[] files = mcachedir.listfiles (); for (File f:files) F.delete ();}}
3. Write Asynchronous Load Component Asyncimageloader
Android uses a single-threaded model that the app executes in the UI main thread. And Android is also the real-time operating system requires timely response or a ANR error occurs. Therefore, for time-consuming operation requirements cannot clog the UI main thread, it is necessary to open a thread processing (such as image loading in this app) and put the thread in the queue, and then notify the UI main thread to make changes when the execution is complete, remove the task at the same time-this is the asynchronous task, Implementing Async in Android can be achieved through the use of Asynctask in this series or using the Thread+handler mechanism, where it is completely written in code, so that we can see more clearly the nature of the implementation of asynchronous communication, code such as the following
public class asyncimageloader{Private memorycache mmemorycache;//Memory cache private Filecache mfilecache;//file cache privat e executorservice mexecutorservice;//thread pool//record ImageView private map<imageview that have been loaded into the picture, string> mimageviews = Collec tions. Synchronizedmap (New Weakhashmap<imageview, string> ());//Save the URL that is loading the picture private List<loadphoto task> mtaskqueue = new arraylist<loadphototask> (); /** * defaults to a thread pool of size 5 * @param context * @param the fast cache used by MemoryCache * @param the file cache used by the Filecache * * Public Asyncimageloader (context context, MemoryCache MemoryCache, Filecache filecache) {Mmemorycache = MemoryCache ; Mfilecache = Filecache; Mexecutorservice = Executors.newfixedthreadpool (5);//build a fixed-size thread pool with a capacity of 5 (maximum number of threads executing)}/** * Load the corresponding picture by URL * @ Param URL * @return take a picture from a first-level cache and then return directly, assuming no asynchronous from the file (level two cache), assuming no more from the network side */public Bitmap LoadBitmap (ImageView image View, String URL) {//record ImageView to M firstThe AP, which indicates that the UI has performed a picture loaded into the Mimageviews.put (ImageView, URL); Bitmap Bitmap = mmemorycache.get (URL);//Get picture if (Bitmap = = null) From first-level cache {Enquequeloadphoto (URL, Imagevie W);//Get the} return bitmap from the level two cache and the network; /** * Add picture download queue * @param URL */private void Enquequeloadphoto (String url, ImageView ImageView) { Assuming the task already exists, do not add the IF (istaskexisted (URL)) return again; Loadphototask task = new Loadphototask (URL, imageView); Synchronized (mtaskqueue) {mtaskqueue.add (Task);//Add task to queue} MEXECUTORSERVICE.EXECU Te (Task);//Submit a task to the thread pool, assuming that there is no upper limit (5), then execute otherwise blocked}/** * Infer if the task already exists in the download queue * @param URL * @return */ Private Boolean istaskexisted (String URL) {if (url = = null) return false; Synchronized (mtaskqueue) {int size = Mtaskqueue.size (); for (int i=0; i<size; i++) {loadphototask task = MTaSkqueue.get (i); if (Task! = null && task.geturl (). Equals (URL)) return true; }} return false; /** * Get picture from cache file or network side * @param URL */private Bitmap getbitmapbyurl (String URL) {File f = m Filecache.getfile (URL);//Gets the cached picture path Bitmap B = Imageutil.decodefile (f);//Gets the Bitmap information of the file if (b! = null)//NOT NULL to get The cached file return B; Return Imageutil.loadbitmapfromweb (URL, f);//Get Picture with network}/** * Infer if the ImageView has already loaded the picture (can be used to infer if it is necessary to load the picture) * @ param imageView * @param url * @return */Private Boolean imageviewreused (ImageView imageView, String URL) {String tag = Mimageviews.get (ImageView); if (tag = = NULL | |!tag.equals (URL)) return true; return false; } private void Removetask (Loadphototask Task) {synchronized (mtaskqueue) {Mtaskqueue.remove (TAS k); }} class Loadphototask implements Runnable {private String URL; Private ImageView ImageView; Loadphototask (String URL, ImageView ImageView) {this.url = URL; This.imageview = ImageView; } @Override public void Run () {if (imageviewreused (ImageView, url)) {//inferred ImageView has been re- Use Removetask (this);//Assume that the task return is deleted if it has been reused; } Bitmap bmp = Getbitmapbyurl (URL);//Get Picture Mmemorycache.put (URL, BMP) from cache file or network side;//Put picture in first level cache if (!imageviewreused (ImageView, url)) {//If the ImageView picture is not added, the picture is displayed in the UI thread bitmapdisplayer BD = new Bitmapdisplaye R (BMP, ImageView, URL); Activity A = (activity) imageview.getcontext (); A.runonuithread (BD);//Invoke the Run method of the BD component on the UI thread to implement the ImageView control load Picture} removetask (this);//Remove the task from the queue} Public String GetUrl () {return URL; }}/** * * is executed by the UI thread in the Run method of the component */class Bitmapdisplayer implements Runnable {private Bitmap Bitmap; Private ImageView ImageView; Private String URL; Public Bitmapdisplayer (Bitmap B, ImageView ImageView, String url) {Bitmap = b; This.imageview = ImageView; This.url = URL; public void Run () {if (imageviewreused (ImageView, URL)) return; if (bitmap! = null) imageview.setimagebitmap (bitmap); }}/** * Release resource */public void Destroy () {mmemorycache.clear (); Mmemorycache = null; Mimageviews.clear (); Mimageviews = null; Mtaskqueue.clear (); Mtaskqueue = null; Mexecutorservice.shutdown (); Mexecutorservice = null; }}
After the writing is complete. It is convenient to call the LoadBitmap () method in Asyncimageloader for asynchronous tasks, and the code for the Asyncimageloader component is best combined with a good look. This will be a deep understanding of the asynchronous communication between Android threads.
4, Tool class Imageutil
public class Imageutil {/** * Gets the picture from the network and caches it in the specified file * @param url image URL * @param file cache * @return * /public static Bitmap loadbitmapfromweb (String url, file file) {HttpURLConnection conn = null; InputStream is = null; OutputStream OS = null; try {Bitmap Bitmap = null; URL imageUrl = new URL (URL); conn = (httpurlconnection) imageurl.openconnection (); Conn.setconnecttimeout (30000); Conn.setreadtimeout (30000); Conn.setinstancefollowredirects (TRUE); is = Conn.getinputstream (); OS = new FileOutputStream (file); CopyStream (is, OS);//cache the picture to disk bitmap = DecodeFile (file); return bitmap; } catch (Exception ex) {ex.printstacktrace (); return null; } finally {try {if (OS! = null) os.close (); if (is! = null) is.close (); IF (conn! = NULL) Conn.disconnect (); } catch (IOException e) {}}} public static Bitmap DecodeFile (File f) {try {Retu RN Bitmapfactory.decodestream (new FileInputStream (f), NULL, NULL); } catch (Exception e) {} return null; } private static void CopyStream (InputStream is, OutputStream OS) {final int buffer_size = 1024; try {byte[] bytes = new Byte[buffer_size]; for (;;) {int count = is.read (bytes, 0, buffer_size); if (count = =-1) break; Os.write (bytes, 0, count); }} catch (Exception ex) {ex.printstacktrace (); } }}
Timing diagram between components:
1, write Mainactivity
public class Mainactivity extends Activity { ListView list; Listviewadapter adapter; @Override public void OnCreate (Bundle savedinstancestate) { super.oncreate (savedinstancestate); Setcontentview (r.layout.main); List= (ListView) Findviewbyid (r.id.list); Adapter=new Listviewadapter (this, mstrings); List.setadapter (adapter); } public void OnDestroy () { list.setadapter (null); Super.ondestroy (); Adapter.destroy (); } Private string[] mstrings={"http://news.21-sun.com/UserFiles/x_Image/x_20150606083511_0.jpg", "http// News.21-sun.com/userfiles/x_image/x_20150606082847_0.jpg ",.....};}
2. Write the adapter
public class Listviewadapter extends Baseadapter {private Activity mactivity; Private string[] data; private static Layoutinflater Inflater=null; Private Asyncimageloader imageloader;//Async component Public Listviewadapter (Activity mactivity, string[] d) {This.mactiv ity=mactivity; Data=d; Inflater = (layoutinflater) mactivity.getsystemservice (Context.layout_inflater_service); MemoryCache mcache=new memorycache ();//Memory cache File SDcard = Android.os.Environment.getExternalStorageDirectory ();// Get SD card File Cachedir = new file (sdcard, "Jereh_cache");//cache root folder Filecache fcache=new Filecache (mactivity, CacheD IR, "news_img");//File Cache Imageloader = new Asyncimageloader (mactivity, Mcache,fcache); } public int GetCount () {return data.length; } public Object GetItem (int position) {return position; } public long Getitemid (int position) {return position; } public view GetView (int position, view Convertview,ViewGroup parent) {Viewholder vh=null; if (convertview==null) {Convertview = inflater.inflate (R.layout.item, NULL); Vh=new Viewholder (); Vh.tvtitle= (TextView) Convertview.findviewbyid (R.id.text); Vh.ivimg= (ImageView) Convertview.findviewbyid (r.id.image); Convertview.settag (VH); }else{vh= (Viewholder) Convertview.gettag (); } vh.tvTitle.setText ("header information Test ————" +position); Vh.ivImg.setTag (Data[position]); Asynchronously loads a picture. First cache, then level two cache, the last network to obtain the picture Bitmap BMP = Imageloader.loadbitmap (vh.ivimg, data[position]); if (BMP = = null) {Vh.ivImg.setImageResource (r.drawable.default_big); } else {Vh.ivImg.setImageBitmap (BMP); } return Convertview; } private class viewholder{TextView tvtitle; ImageView ivimg; } public void Destroy () {Imageloader.destroy (); }}
A small partner who wants to learn a lot about other content. You can click to view the source code and perform the test yourself.
Inquiries or technical exchanges, please increase the official QQ group:
idkey=69fd2f84c1212ecb10062430746aa802c93431c006c1d8cd8c34c5dd4f14772d "target=" _blank "> (452379712)
Jerry Education
Source:http://blog.csdn.net/jerehedu/
This article belongs to Yantai Jerry Education Technology Co., Ltd. and CSDN co-owned, welcome reprint. However, this statement must be retained without the author's permission. And in the article page obvious location to the original link, otherwise reserves the right to pursue legal responsibility.
Copyright notice: This article Bo Master original articles, blogs, without consent may not be reproduced.
Android Batch Image loading classic series--using level two cache, asynchronous network payload image