The previous article simply introduced the use and optimization of the ListView, which are some common optimization techniques. But the ListView optimization also has some important problems, that is, the picture loading, asynchronous loading optimization, because the picture occupies a large memory, the ListView in the sliding process is prone to the phenomenon of oom, I would like to explain to you an asynchronous loading optimization ideas.
In general there are a few optimization ideas:
1, the ImageView using the Settag () method to solve the problem of image dislocation, this tag is set in the URL of the image, and then in the loading of the URL and to load the URL of the position in the comparison, if not the same load, the same is reused before the load
2, for the picture resources to be loaded, first in the memory cache (the original method is to use Softrefrence, the latest method is to use Android-provided LRUCache), if not found, in the local cache (you can use the Disklrucache Class) to find ( That is, read the original downloaded local image), and can not find, open the asynchronous thread to download the picture, after downloading, saved locally, the memory cache also retains a copy of the reference
3, in an asynchronous thread, measure the size of the picture you want, scale it proportionally
4, using a map to save the asynchronous thread of the reference, Key->value to url->asynctask, so as to avoid the thread has been opened to load the picture, but have not finished loading, and then repeatedly open the thread to load the picture case
5, in the fast sliding time do not load the picture, pause all the pictures loaded thread, once stopped, the end of the invisible picture of the loading thread, continue to see the picture of the loading thread
Here are some of the examples I've picked up online, and I've introduced them to illustrate the above-mentioned optimization ideas
A first example:
public class MemoryCache {private static final String TAG = "memorycache";//The last parameter of a synchronous operation//Linkedhashmap constructor method when put in cache true represents The elements in this Map will be arranged according to the number of recent uses, that is, lru//the advantage is that if you want to replace the elements in the cache, the least recently used elements are traversed first to replace them to improve efficiency private map<string, bitmap> cache = Collections.synchronizedmap (New linkedhashmap<string, bitmap> (1.5f, True));//cache in bytes occupied by the picture, the initial 0, The heap memory occupied by the cache is strictly controlled by this variable private long size = 0;//Current Allocated size//cache can only occupy the maximum heap memory private long limit = 1000000;//Max Memor Y in Bytespublic memorycache () {//Use 25% of available heap Sizesetlimit (Runtime.getruntime (). MaxMemory ()/4);} public void Setlimit (long new_limit) {limit = New_limit; LOG.I (TAG, "memorycache'll use" + limit/1024. /1024. + "MB");} Public Bitmap get (String id) {try {if (!cache.containskey (ID)) return null;return cache.get (ID);} catch ( NullPointerException ex) {return null;}} public void put (String ID, Bitmap Bitmap) {try {if (Cache.containskey (ID)) Size-= Getsizeinbytes (Cache.get (id)); Cache.put (ID, bitmap); size + = GetsizeiNbytes (bitmap); Checksize ();} catch (Throwable th) {th.printstacktrace ();}} /** * Strict control over heap memory, if more than the least recently used picture cache will be replaced first * */private void Checksize () {log.i (TAG, "cache size=" + size + "length=" + cache.s Ize ()), if (Size > Limit) {//First traverse the least recently used element iterator<entry<string, bitmap>> iter = Cache.entryset (). Iterator (); while (Iter.hasnext ()) {entry<string, bitmap> Entry = Iter.next (); Size-= Getsizeinbytes ( Entry.getvalue ()); Iter.remove (); if (size <= limit) break;} LOG.I (TAG, "clean cache. New size "+ cache.size ());}} public void Clear () {cache.clear ();} /** * Image Occupied memory * * @param bitmap * @return */long getsizeinbytes (bitmap bitmap) {if (bitmap = null) return 0;return bitmap . Getrowbytes () * Bitmap.getheight ();}}
You can also use SoftReference, the code will be much simpler, but I recommend the method above.
public class MemoryCache {private map<string, softreference<bitmap>> cache = Collections.synchronizedmap ( New hashmap<string, softreference<bitmap>> ());p ublic Bitmap get (String ID) {if (!cache.containskey (ID)) return null; Softreference<bitmap> ref = Cache.get (ID); return ref.get ();} public void put (String ID, Bitmap Bitmap) {cache.put (ID, new softreference<bitmap> (Bitmap));} public void Clear () {cache.clear ();}}
The following is the code Filecache.java for the file cache class:
public class Filecache {private File cachedir;public Filecache (Context context) {//If there is an SD card in the SD card to build a lazylist directory to hold the cached picture// No SD card is placed in the system's cache directory if (android.os.Environment.getExternalStorageState (). Equals (android.os.Environment.MEDIA_ Mounted)) Cachedir = new File (Android.os.Environment.getExternalStorageDirectory (), "lazylist"); Elsecachedir = Context.getcachedir (); if (!cachedir.exists ()) Cachedir.mkdirs ();} Public file GetFile (string url) {//URL hashcode as the cached file name String filename = string.valueof (Url.hashcode ());//Another Possible solution//String filename = urlencoder.encode (URL); File F = new file (cachedir, filename); return f;} public void Clear () {file[] files = cachedir.listfiles (); if (files = = null) return;for (File f:files) F.delete ();}}
Finally the most important class to load the picture, Imageloader.java:
public class Imageloader {memorycache memorycache = new MemoryCache (); Filecache filecache;private Map<imageview, string> imageviews = Collections.synchronizedmap (New WeakHashMap< ImageView, string> ());//thread pool Executorservice executorservice;public Imageloader (context context) {Filecache = new Filecache (context); Executorservice = Executors.newfixedthreadpool (5);} When entering the ListView default picture, can be replaced by your own default picture final int stub_id = r.drawable.stub;//The Main method public void displayimage (String URL, ImageView ImageView) {imageviews.put (ImageView, URL);//First find bitmap bitmap = memorycache.get (URL) from the memory cache; if (bitmap! = null Imageview.setimagebitmap (bitmap), else {//if not, open a new line loads download the image queuephoto (URL, imageView); Imageview.setimageresource ( stub_id);}} private void Queuephoto (String url, ImageView ImageView) {phototoload p = new Phototoload (URL, ImageView); executorservice . Submit (New Photosloader (P));} Private Bitmap getbitmap (String url) {file F = filecache.getfile (URL);//Find out if there is Bitmap B = DecodeFile (f) from the file cache; if (b! =NULL) return b;//finally download the picture from the specified URL try {Bitmap Bitmap = null; URL imageUrl = new URL (URL); HttpURLConnection conn = (httpurlconnection) imageurl.openconnection (); Conn.setconnecttimeout (30000); Conn.setreadtimeout (30000); Conn.setinstancefollowredirects (true); InputStream is = Conn.getinputstream (); O Utputstream OS = new FileOutputStream (f); CopyStream (is, OS); Os.close (); bitmap = DecodeFile (f); return bitmap;} catch (Exception ex) {ex.printstacktrace (); return null;}} Decode this picture and scaling to reduce memory consumption, the cache size of the virtual machine for each picture is also limited by private Bitmap DecodeFile (File f) {try {//decode image Sizebitmapfactory.options o = new Bitmapfactory.options (); o.injustdecodebounds = true; Bitmapfactory.decodestream (new FileInputStream (f), NULL, O);//Find the correct scale value. It should be the power of 2.final int required_size = 70;int width_tmp = o.outwidth, height_tmp = o.outheight;int scale = 1;while (True) {if (Width_tmp/2 < required_size| | HEIGHT_TMP/2 < required_size) break;width_tmp/= 2;height_tmp/ = 2;scale *= 2;}Decode with insamplesizebitmapfactory.options O2 = new Bitmapfactory.options (); o2.insamplesize = Scale;return Bitmapfactory.decodestream (new FileInputStream (f), NULL, O2);} catch (FileNotFoundException e) {}return null;} Task for the Queueprivate class Phototoload {public string url;public ImageView imageview;public phototoload (String u, ImageView i) {url = U;imageview = i;}} Class Photosloader implements Runnable {Phototoload phototoload; Photosloader (Phototoload phototoload) {this.phototoload = Phototoload;} @Overridepublic void Run () {if (imageviewreused (phototoload)) return; Bitmap bmp = Getbitmap (Phototoload.url); Memorycache.put (Phototoload.url, BMP); if (imageviewreused (phototoload)) Return Bitmapdisplayer bd = new Bitmapdisplayer (BMP, Phototoload);//The updated operation is placed in the UI thread activity A = (activity) PhotoToLoad.imageView.getContext (); A.runonuithread (BD);}} /** * Prevent picture dislocation * * @param phototoload * @return */boolean imageviewreused (phototoload phototoload) {String tag = imageviews . Get (phototoload.imAgeview); if (tag = = NULL | |!tag.equals (PHOTOTOLOAD.URL)) return True;return false;} Used to update the interface in the UI thread class Bitmapdisplayer implements Runnable {Bitmap Bitmap; Phototoload phototoload;public Bitmapdisplayer (Bitmap B, Phototoload p) {Bitmap = B;phototoload = P;} public void Run () {if (imageviewreused (phototoload)) return;if (bitmap! = null) PhotoToLoad.imageView.setImageBitmap ( bitmap); ElsephotoToLoad.imageView.setImageResource (stub_id);}} public void ClearCache () {memorycache.clear (); Filecache.clear ();} public 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) {}}}
The idea of the above code is this, first is a memorycache class, used to cache the image applied to memory. This class contains a collectiosn.synchronizedmap (new linkedhashmap<string,bitmap> (10,1.5f,true)) object, This object is used to save the URL and the corresponding bitmap, that is, the cache, the last parameter set to True is the reason that the elements in this map will be in accordance with the most recent use of the number from less to more than the permutation, namely LRU. The benefit is that if you want to replace the elements in the cache, you will first go through the least recently used elements to replace them to improve efficiency.
Also set a maximum value of the cache limit, and an initial value of size=0. Each time you add a picture cache, size increases the size, and if you increase the size beyond limit, traverse the linkedhashmap to clear the least-used cache while reducing the size value until size<limit.
The authors also cite an example of using SoftReference, the advantage of which is that Android will automatically reclaim the appropriate bitmap cache for us.
Next is the file cache, if there is an SD card in the SD card to build a lazylist directory to hold the cached picture, no SD card is placed in the system's cache directory, the URL hashcode as the cached file name. This class simply creates and returns a file class based on the URL name, without a real cached picture, and the picture is cached in the Imageloader class, but the class wants to get the file returned by Filecache to make the FileOutputStream destination.
Finally, the responsible Imageloader, this class has a thread pool for managing download threads. There is also a weakhashmap<imageview, string> used to save ImageView references and record tags for image updates. It checks the cache first, does not open a thread to download, after downloading the picture saved to the cache (memory, file), and then scale the image scale, return a suitable size bitmap, and finally open a thread to the new UI (The Way is Imagview.getcontext () Gets the corresponding context, and then the context calls the Runonuithread () method).
In addition, before the download thread is opened, after the picture download is complete, with the new UI, all through Weakhashmap<imageview, string> get the download image of the tag and corresponding to set the image ImageView tag comparison, to prevent image dislocation.
The above code completes the basic optimization idea, even uses a own definition cache class MemoryCache, makes the management become clearer, simultaneously has the file cache, also through Imagview->url way avoids the picture dislocation, also opens the asynchronous thread to download the picture, But it opens a UI thread to go with the new UI.
The disadvantage is that the UI thread is turned on to update the UI, wasting resources, which can be implemented using the definition of a callback interface. There is also no consideration of the issue of repeatedly opening the download thread.
A second example:
Paste the code of the Main method first:
Package Cn.wangmeng.test;import Java.io.ioexception;import Java.io.inputstream;import java.lang.ref.SoftReference; Import Java.net.malformedurlexception;import Java.net.url;import Java.util.hashmap;import Android.graphics.drawable.drawable;import Android.os.handler;import Android.os.message;public Class Asyncimageloader {private hashmap<string, softreference<drawable>> Imagecache; Public Asyncimageloader () {Imagecache = new hashmap<string, softreference<drawable>> (); Public drawable loaddrawable (final String imageUrl, final imagecallback imagecallback) {if (imagecache.co Ntainskey (IMAGEURL)) {softreference<drawable> softreference = Imagecache.get (IMAGEURL); drawable drawable = Softreference.get (); if (drawable! = null) {return drawable; }} final Handler Handler = new Handler () {public void Handlemessage (message message) { Imagecallback.imageloaded ((drawable) message.obj, IMAGEURL); } }; New Thread () {@Override public void run () {drawable drawable = Loadimagefromurl ( IMAGEURL); Imagecache.put (IMAGEURL, New softreference<drawable> (drawable)); Message message = Handler.obtainmessage (0, drawable); Handler.sendmessage (message); }}.start (); return null; } public static drawable loadimagefromurl (String url) {URL M;inputstream i = null;try {m = new URL (URL); i = (InputStream) M.getcontent ();} catch (Malformedurlexception E1) {e1.printstacktrace ();} catch (IOException e) {e.printstacktrace ();} drawable d = drawable.createfromstream (i, "src"); return D;} Public interface Imagecallback {public void imageloaded (drawable imagedrawable, String imageUrl); }}
The above code is the main method to achieve the asynchronous acquisition of pictures, SoftReference is a soft reference, is to better for the system to reclaim variables, duplicate URLs directly return the existing resources, implement the callback function, let the data succeed, update to the UI thread.
Several auxiliary class files:
Package Cn.wangmeng.test;public class ImageAndText { private String imageUrl; private String text; Public ImageAndText (String imageUrl, string text) { this.imageurl = IMAGEURL; This.text = text; } Public String Getimageurl () { return imageUrl; } Public String GetText () { return text; }}
Package Cn.wangmeng.test;import Android.view.view;import Android.widget.imageview;import Android.widget.TextView; public class Viewcache { private View baseview; Private TextView TextView; Private ImageView ImageView; Public Viewcache (View baseview) { this.baseview = Baseview; } Public TextView Gettextview () { if (TextView = = null) { TextView = (TextView) Baseview.findviewbyid (R.id.text); c9/>} return textView; } Public ImageView Getimageview () { if (ImageView = = null) { ImageView = (ImageView) Baseview.findviewbyid ( r.id.image); } return imageView;} }
Viewcache is a sub-element layout that assists in getting adapter
Package Cn.wangmeng.test;import Java.util.list;import Cn.wangmeng.test.asyncimageloader.imagecallback;import Android.app.activity;import Android.graphics.drawable.drawable;import Android.view.layoutinflater;import Android.view.view;import Android.view.viewgroup;import Android.widget.arrayadapter;import Android.widget.imageview;import Android.widget.listview;import Android.widget.textview;public Class Imageandtextlistadapter extends Arrayadapter<imageandtext> {private ListView listview; Private Asyncimageloader Asyncimageloader; Public Imageandtextlistadapter (activity activity, List<imageandtext> imageandtexts, ListView listview) {Supe R (activity, 0, imageandtexts); This.listview = ListView; Asyncimageloader = new Asyncimageloader (); Public View GetView (int position, View Convertview, ViewGroup parent) {Activity activity = (activity) Getcont Ext (); Inflate the views from XML View Rowview = Convertview; VieWcache Viewcache; if (Rowview = = null) {Layoutinflater inflater = Activity.getlayoutinflater (); Rowview = inflater.inflate (R.layout.image_and_text_row, NULL); Viewcache = new Viewcache (Rowview); Rowview.settag (Viewcache); } else {Viewcache = (Viewcache) rowview.gettag (); } imageandtext ImageAndText = GetItem (position); Load the image and set it on the ImageView String imageUrl = Imageandtext.getimageurl (); ImageView ImageView = Viewcache.getimageview (); Imageview.settag (IMAGEURL); drawable cachedimage = asyncimageloader.loaddrawable (ImageUrl, New Imagecallback () {public void imageloaded (Dr Awable imagedrawable, String imageUrl) {ImageView Imageviewbytag = (ImageView) Listview.findviewwithtag (IM Ageurl); if (Imageviewbytag! = null) {imageviewbytag.setimagedrawable (imagedrawable); } }}); if (cachedimage = = null) {Imageview.setimageresource (r.drawable.default_image);} Else{imageview.setimagedrawable (cachedimage);} Set the text on the TextView TextView TextView = Viewcache.gettextview (); Textview.settext (Imageandtext.gettext ()); return rowview; }}
The idea of the above code is this:Asyncimageloader class, using a hashmap<string, softreference<drawable>> used to cache , then there is an asynchronous download thread, there is a method inside the handler, after the thread download is complete, a message is sent to handler, and then handler calls the callback interface imagecallback imageloaded( ) method, this method is implemented in adapter, so that is the main thread and the new UI.
And the Viewcache class is actually Viewholder,imageandtext is a bean class.
in Adapter, Mageview.settag (IMAGEURL) is used to provide a unique identifier URL for ImageView, so after the picture download is complete, Imagecallback in the imageloaded() method, you can call the ListView Findviewwithtag (IMAGEURL) To find the corresponding ImageView, so as not to worry about the wrong problem, this method is more ingenious.
The disadvantage is that the file cache is not implemented, and there is no problem solving multiple threads downloading the same picture.
Use and optimization of the ListView (medium)