In App application, ListView's asynchronous loading image can bring good user experience, and it is also an important index to consider program performance. On the ListView asynchronous loading, there are many examples on the web, the central idea is similar, but many versions or have bugs, or performance problems to be optimized. In view of this, I found a relatively ideal version on the Internet and on the basis of the transformation, let me elaborate on the following principles to explore the mysteries of the secret, and the gentlemen to share the reward ...
Paste the effect chart first:
load the picture in asynchronous basic idea:
1. Get the picture display (memory buffer) from the memory cache first
2. Get the words from the SD card (SD card buffer)
3. If you can't get it, download the picture from the network and save it to the SD card and add the memory and display (see if you want to display it)
OK, first on the adapter code:
Copy Code code as follows:
public class Loaderadapter extends baseadapter{
private static final String TAG = "Loaderadapter";
Private Boolean mbusy = false;
public void Setflagbusy (Boolean busy) {
This.mbusy = busy;
}
Private Imageloader Mimageloader;
private int mcount;
Private context Mcontext;
Private string[] urlarrays;
public Loaderadapter (int count, context context, String []url) {
This.mcount = count;
This.mcontext = context;
urlarrays = URL;
Mimageloader = new Imageloader (context);
}
Public Imageloader Getimageloader () {
return mimageloader;
}
@Override
public int GetCount () {
return mcount;
}
@Override
Public Object getitem (int position) {
return position;
}
@Override
public long getitemid (int position) {
return position;
}
@Override
Public View GetView (int position, View Convertview, ViewGroup parent) {
Viewholder viewholder = null;
if (Convertview = = null) {
Convertview = Layoutinflater.from (Mcontext). Inflate (
R.layout.list_item, NULL);
Viewholder = new Viewholder ();
Viewholder.mtextview = (TextView) convertview
. Findviewbyid (R.id.tv_tips);
Viewholder.mimageview = (ImageView) convertview
. Findviewbyid (R.id.iv_image);
Convertview.settag (Viewholder);
} else {
Viewholder = (Viewholder) convertview.gettag ();
}
String url = "";
url = urlarrays[position% urlarrays.length];
ViewHolder.mImageView.setImageResource (R.drawable.ic_launcher);
if (!mbusy) {
Mimageloader.displayimage (URL, Viewholder.mimageview, false);
ViewHolder.mTextView.setText ("--" + position
+ "--idle | | Touch_scroll ");
} else {
Mimageloader.displayimage (URL, Viewholder.mimageview, true);
ViewHolder.mTextView.setText ("--" + position + "--fling");
}
return convertview;
}
Static Class Viewholder {
TextView Mtextview;
ImageView Mimageview;
}
}
The key code is the DisplayImage method of Imageloader, and then see the realization of Imageloader
Copy Code code as follows:
public class Imageloader {
Private MemoryCache memorycache = new MemoryCache ();
Private Abstractfilecache Filecache;
Private Map<imageview, string> imageviews = Collections
. Synchronizedmap (New Weakhashmap<imageview, string> ());
Thread pool
Private Executorservice Executorservice;
Public Imageloader {
Filecache = new Filecache (context);
Executorservice = Executors.newfixedthreadpool (5);
}
The most important way
public void displayimage (String url, ImageView imageview, Boolean Isloadonlyfromcache) {
Imageviews.put (ImageView, URL);
Find from the memory cache first
Bitmap Bitmap = memorycache.get (URL);
if (bitmap!= null)
Imageview.setimagebitmap (bitmap);
else if (!isloadonlyfromcache) {
If not, turn on the new line Chengga to download the picture
Queuephoto (URL, imageview);
}
}
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);
To find out from the file cache first whether there are
Bitmap B = null;
if (f!= null && f.exists ()) {
b = DecodeFile (f);
}
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 ();
OutputStream OS = new FileOutputStream (f);
CopyStream (is, OS);
Os.close ();
Bitmap = DecodeFile (f);
return bitmap;
catch (Exception ex) {
LOG.E ("", "Getbitmap catch exception...\nmessage =" + ex.getmessage ());
return null;
}
}
Decode this picture and scaling it proportionally to reduce memory consumption, the virtual machine has a limited cache size for each picture.
Private Bitmap DecodeFile (File f) {
try {
Decode Image size
Bitmapfactory.options o = new Bitmapfactory.options ();
O.injustdecodebounds = true;
Bitmapfactory.decodestream (new FileInputStream (f), NULL, O);
Find the correct scale value. It should is the power of 2.
Final int required_size = 100;
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 Insamplesize
Bitmapfactory.options O2 = new Bitmapfactory.options ();
O2.insamplesize = scale;
Return Bitmapfactory.decodestream (New FileInputStream (f), NULL, O2);
catch (FileNotFoundException e) {
}
return null;
}
Task for the queue
Private 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;
}
@Override
public 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 action 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);
}
}
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) {
LOG.E ("", "CopyStream catch Exception ...");
}
}
}
First load from memory, no open thread from the SD card or the network to obtain, here note from the SD card to get the picture is placed in the child thread, otherwise the speed skating screen will not be fluent, this is optimized. At the same time, in the adapter has a busy variable, indicating whether the ListView is in the sliding state, if it is sliding state only from the memory to get the picture, there is no need to open the thread to the external storage or network to get pictures, which is optimized two. Threads in the Imageloader use a thread pool, which avoids the frequent creation and destruction of too many threads, and the fact that some children's shoes are always new to execute this is very undesirable, a good point of the use of the Asynctask class, the internal is also used in the thread pool. When you get a picture from the network, first save it to the SD card, and then load it into memory, the advantage is that you can do a compression when loading into memory to reduce the memory of the picture, which is optimized by three.
And the nature of the problem of image dislocation stems from our listview using a cache Convertview, assuming a scene, a ListView screen display nine item, then when pulling out the tenth item, the fact that the item is repeated use of the first item, That is, when the first item downloads a picture from the network and eventually displays it, the item is no longer in the current display area, and the result is that it will be displayed on the tenth item, which leads to the problem of image dislocation. So the solution is that visible is displayed, not visible, not displayed. In the Imageloader, there is a Imageviews map object, which is used to save the current display area image corresponding to the set of URLs, before the display to determine the processing.
Below again the memory buffer mechanism, this example uses is the LRU algorithm, first looks at the memorycache realization
Copy Code code as follows:
public class MemoryCache {
private static final String TAG = "MemoryCache";
A synchronization operation when putting in the cache
The last argument of the Linkedhashmap constructor true means that the elements in this map will be sorted by the number of recent uses, that is, the LRU
The advantage is that if you want to replace the elements in the cache, you can replace them with the least recently used element to improve efficiency.
Private map<string, bitmap> cache = Collections
. Synchronizedmap (New linkedhashmap<string, bitmap> (1.5f, true));
The byte occupied by the picture in the cache, initial 0, which will control the heap memory occupied by the cache strictly
Private long size = 0;//Current Allocated size
Maximum heap memory that can be consumed by caching
Private long limit = 1000000;//max memory in bytes
Public MemoryCache () {
Use 25% of available heap size
Setlimit (Runtime.getruntime (). MaxMemory ()/10);
}
public void Setlimit (long new_limit) {
Limit = New_limit;
LOG.I (TAG, "memorycache'll use up to" + 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 of heap memory, if more than will first replace the least recently used image cache
*
*/
private void Checksize () {
LOG.I (TAG, "cache size=" + size + "length=" + cache.size ());
if (Size > limit) {
First iterate through the least recently used elements
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 ();
}
/**
* The memory occupied by the picture
*
* <a href= ' http://www.eoeandroid.com/home.php?mod=space&uid=2768922\ ' target= ' _blank\ ' > @Param < /a> bitmap
*
* @return
*/
Long Getsizeinbytes (Bitmap Bitmap) {
if (bitmap = null)
return 0;
return Bitmap.getrowbytes () * Bitmap.getheight ();
}
}
First limit memory picture buffer heap memory size, each time there is a picture to the cache overtime to determine whether to exceed the limit size, more than the use of the minimum to remove the picture and remove it, of course, if this is not used here, for soft reference is also feasible, Both purposes are to maximize the use of existing in memory of the image cache, to avoid duplication of garbage to increase the GC burden, oom overflow is often due to a large amount of memory instantaneous and garbage collection caused by the delay. But the difference is that the image cache in the Linkedhashmap is not retrieved before it is removed, and the image cache in SoftReference is collected by GC at any time without any other references saved. So in the use of Linkedhashmap this LRU algorithm cache is more conducive to the effective hit of the picture, of course, with the use of the words better, that is, removed from the Linkedhashmap cache into the SoftReference, which is the memory of the level two cache, Interested children's shoes extraordinary try.