Continued: loading images by using ListView reuse, and reloading by using listview
Recently, I have adopted a dog for a long time. According to the dog owner, it is just Alaska. Please witness it.
Whether it's Alaska or not, I still have to raise it and name it "egg ".
Continue to talk about technology.
Loading images in listview is always an endless topic.
If an image needs to be downloaded for each item in the listview, we have to consider the out of memory problem caused by loading a large number of images.
A typical practice is to check whether the image is in the cache when downloading the image. If the image is not in the cache, read it from the SD card. If the image is not in the cache, download it from the server, the downloaded image is first placed in the SD card and cached. This can be repeated.
This involves how to design the cache. A common practice is to use the LRU algorithm to cache images. First, set a memory area on the mobile phone to cache images, then we will drop the downloaded image in the form of a key-value pair, so that we can get the corresponding image, and the speed is very fast. You must know that reading images from memory is faster than reading files.
The LRU cache code is as follows:
/*** Image cache Note: The image cache uses the classic LRU algorithm. Each file corresponds to a key and the key is used to locate the cached image. ** note that, all the keys obtained are image URLs and values are drawable types. * to ensure the uniformity of keys, the image URLs are uniformly encrypted using md5, that is to say, all keys are 32-bit strings ** @ author kyson **/public class ImageCache {private static final String TAG = "ImageCache "; // obtain the maximum memory allocated by the system to each application. Each application system allocates 32 Mprivate static int maxMemory = (int) (Runtime. getRuntime (). maxMemory ()/1024); // allocate 1/8 4 Mprivate static int cacheSize to LruCache = MaxMemory/8; private static ImageCache instance; private static LruCache <String, Bitmap> mLruCache = new LruCache <String, Bitmap> (cacheSize) {// release upon deletion // public boolean isRecycleWhenRemove = true; @ Overrideprotected int sizeOf (String key, Bitmap value) {// TODO Auto-generated method stub/return super. sizeOf (key, value); return value. getByteCount ()/1024;} @ Overrideprotected void entryRemoved (bo Olean evicted, String key, Bitmap oldValue, Bitmap newValue) {super. entryRemoved (evicted, key, oldValue, newValue); Log. I ("------", "Maximum system memory:" + maxMemory); if (evicted & null! = OldValue &&! OldValue. isRecycled () {oldValue. recycle (); oldValue = null ;}/ *** create an image based on the given key */@ Overrideprotected Bitmap create (String key) {Bitmap bitmap = null; fileHandler fileHandler = new FileHandler (using application. getContext (); File file = fileHandler. findFileByName (key, fileHandler. getImagePath (); if (file! = Null) {bitmap = ImageUtils. readFileToBitmapWithCompress (file. getAbsolutePath (); return bitmap;} return null ;};}; public static ImageCache reset instance () {if (instance = null) {synchronized (ImageCache. class) {instance = new ImageCache () ;}} return instance ;}private ImageCache () {}/ *** load the image. If no, load the default image ** @ param resId * @ param imageView */public Bitmap loadImage (String imageUrlString) {Bitmap bit Map = null; if (! TextUtils. isEmpty (imageUrlString) {String imageKey = MD5Encoder. encoding (imageUrlString); System. out. println ("key:" + imageKey); bitmap = mLruCache. get (imageKey);} if (bitmap! = Null &&! Bitmap. isRecycled () {Log. I (TAG, "Find the corresponding resource in the cache pool and directly return the existing bitmap resource"); return bitmap;} else {// default image R. drawable. ic_menu_addLog. I (TAG, "the corresponding resource is not found in the cache pool, and the returned bitmap is null"); return null ;}} /*** cached image *** @ param imageNameString * file name (excluding path) */public void cacheImage (Bitmap bitmap, String fileUrlString) {if (fileUrlString = null | bitmap = null) {Log. I (TAG, "parameter input error, fileNameString or bitmap is empty"); return;} if (bitmap! = Null) {String imageKey = MD5Encoder. encoding (fileUrlString); mLruCache. put (imageKey, bitmap); Log. I (TAG, "cache successful! The cached image url is: "+ fileUrlString +" corresponding key: "+ imageKey);} elseLog. I (TAG," cache failed! Corresponding resource already exists in the cache pool ");}/*** @ param file */public void cacheImage (File file, String fileUrlString) {if (file = null | fileUrlString = null) {return;} // Drawable drawable = Drawable. bitmap bitmap2 = loadImage (fileUrlString); if (bitmap2 = null) {// Drawable. createFromStream (inputStream, null); // cache the files in the folder Bitmap bitmap = ImageUtils. readFileToBitmapWithCompress (file. getAbsolutePath (); cacheImage (bitmap, FileUrlString);} else {System. out. println ("cache failed! The corresponding resource already exists in the cache pool ");}}}
When cache is a problem, you can see the following code to solve it:
/*** Image downloader class **/public class ImageDownLoad {private static final String TAG = "ImageDownLoad"; ImageDownloadListener mListener = null; private static int ALIVE_TIME = 30; // number of core threads private static int CORE_SIZE = 5; // maximum number of tasks private static int MAX_SIZE = 15; private static ArrayBlockingQueue <Runnable> runnables = new ArrayBlockingQueue <Runnable> (25); private static ThreadFactory factory = Executors. defaultT HreadFactory (); // thread pool private static ThreadPoolExecutor mImageThreadPool; private ImageCache mImageCache; private FileHandler mFileHandler; private Context mContext; private static ImageDownLoad instance; public static ImageDownLoad consumer instance (Context context) {if (instance = null) {synchronized (ImageCache. class) {instance = new ImageDownLoad (context) ;}return instance ;}public ImageDownLoad (Context Context) {mContext = context; mFileHandler = new FileHandler (context); mImageThreadPool = getThreadPool (); mImageCache = ImageCache. concurrent instance ();} public void cancelAllTasks () {}/*** method for obtaining the thread pool, we add the synchronization lock ** @ return */public ThreadPoolExecutor getThreadPool () {if (mImageThreadPool = null) {synchronized (ThreadPoolExecutor. class) {if (mImageThreadPool = null) {// to download images more smoothly, we use two threads to download images. MImageThreadPool = new ThreadPoolExecutor (CORE_SIZE, MAX_SIZE, ALIVE_TIME, TimeUnit. SECONDS, runnables, factory, new ThreadPoolExecutor. discardOldestPolicy () ;}} return mImageThreadPool;}/*** obtain a Bitmap from the memory cache ** @ param key * @ return */public Bitmap getBitmapFromMemCache (String key) {return mImageCache. loadImage (key);}/*** add Bitmap to memory cache ** @ param key * @ param bitmap */public void addBitmapToMem OryCache (String key, Bitmap bitmap) {if (getBitmapFromMemCache (key) = null & bitmap! = Null) {mImageCache. cacheImage (bitmap, key) ;}}/*** download */public void downloadImageByUrl (final String imageUrlString, final ImageDownloadListener listener) {if (imageUrlString = null) {Log. I (TAG, "downloadImageByUrl imageUrlString is null"); return;} final String subUrl = MD5Encoder. encoding (imageUrlString); final Handler handler = new Handler () {@ Overridepublic void handleMessage (Message msg) {super. HandleMessage (msg); // callback if (msg. obj! = Null) {listener. imageDownloadFinished (Bitmap) msg. obj, imageUrlString); File file = mFileHandler. createEmptyFileToDownloadDirectory (subUrl); ImageUtils. writeBitmap2File (Bitmap) msg. obj, file); addBitmapToMemoryCache (imageUrlString, (Bitmap) msg. obj);} else {Log. I (TAG, "Download succeeded, but the image is blank !!!!! ") ;}}; // Export cute (new Runnable () {// download file @ Overridepublic void run () {Bitmap bitmap = getBitmapFormUrl (imageUrlString); if (bitmap! = Null) {Message msg = handler. obtainMessage (); msg. obj = bitmap; handler. sendMessage (msg); // Add Bitmap to the memory cache }}});} /*** download and return the Image Based on the url ** @ param url * @ return */private Bitmap getBitmapFormUrl (String url) {Bitmap bitmap = null; // initialize the image download FileDownLoad fileDownLoad = new FileDownLoad (mContext); // download the image and return whether the File file = fileDownLoad is successful. downloadByUrl (url); // if the download is successful, find the image in the specified path, cache the image, and load the image if (file! = Null) {Log. I (TAG, "image download successful! Using FileDownLoad "); try {Log. I ("wenjuan", "image download to file" + file. getPath (); bitmap = BitmapFactory. decodeFile (file. getPath ();} catch (OutOfMemoryError err) {Log. I ("<>", "decodefile"); Runtime. getRuntime (). gc (); bitmap = null ;}} else {Log. I (TAG, "image download failed! Download the image again and call the built-in download tool "); bitmap = getBitmapFormUrlWithHttpURLConnection (url);} return bitmap ;} /*** file downloader ** @ param url * @ return */private Bitmap getBitmapFormUrlWithHttpURLConnection (String url) {Bitmap bitmap = null; HttpURLConnection con = null; try {URL mImageUrl = new URL (url); con = (HttpURLConnection) mImageUrl. openConnection (); con. setConnectTimeout (10*1000); con. setReadTimeout (10*1000); Con. setRequestMethod ("GET"); con. setDoInput (true); con. setDoOutput (true); bitmap = BitmapFactory. decodeStream (con. getInputStream ();} catch (FileNotFoundException e) {e. printStackTrace ();} catch (ProtocolException e) {e. printStackTrace ();} catch (IOException e) {e. printStackTrace ();} catch (OutOfMemoryError ee) {Runtime. getRuntime (). gc (); bitmap = null;} finally {if (con! = Null) {con. disconnect () ;}} return bitmap;}/*** set listener ** @ param callback */public void setImageDownloadFinishListener (ImageDownloadListener imageDownloadListener) {this. mListener = imageDownloadListener;}/*** encapsulate message passing Parameters Using ** @ author kyson **/class MessageData {public Bitmap bitmap; public String imageUrlString ;}}
Then, the download is involved.
/*** File Download *** @ author kyson **/public class FileDownLoad {private static final int STATUS_ OK = 200; private FileHandler mFileHandler; public FileDownLoad (Context context) {mFileHandler = new FileHandler (context);}/*** download the compressed file ** @ param urlString * File url * @ return */public boolean downloadByUrlByGzip (String urlString, string fileName) {boolean downloadSucceed = false; AndroidHttpClient httpClient = AndroidHttpClient. newInstance (null); httpClient. getParams (). setParameter ("Accept-Encoding", "gzip"); HttpPost = new HttpPost (urlString); HttpResponse response = null; try {response = httpClient.exe cute (post ); if (response. getStatusLine (). getStatusCode () = STATUS_ OK) {HttpEntity entity = response. getEntity (); File file = mFileHandler. createEmptyFileToDownloadDirectory (fileName); InputStream inputStream = AndroidHttpClient. getUngzippedContent (entity); downloadSucceed = StreamUtil. writeStreamToFile (inputStream, file);} else {System. out. println ("server connection problem") ;}} catch (Exception e) {// TODO Auto-generated catch blocke. printStackTrace ();} finally {httpClient. close () ;}return downloadSucceed ;} /*** download the file to the download directory ** @ param urlString * url address * @ param fileName * specifies the file name * @ return */public boolean downloadByUrl (String urlString, String fileName) {boolean downloadSucceed = false; AndroidHttpClient httpClient = AndroidHttpClient. newInstance (null); // HttpPost post = new HttpPost (urlString); HttpGet get = new HttpGet (urlString); HttpResponse response = null; try {response = httpClient.exe cute (get ); if (response. getStatusLine (). getStatusCode () = STATUS_ OK) {HttpEntity entity = response. getEntity (); File file = mFileHandler. createEmptyFileToDownloadDirectory (fileName); entity. writeTo (new FileOutputStream (file); downloadSucceed = true;} else {System. out. println ("File Download: server connection problems") ;}} catch (Exception e) {// TODO Auto-generated catch blocke. printStackTrace ();} finally {httpClient. close ();} return downloadSucceed;}/*** download and return the File ** @ param urlString * @ return */public File downloadByUrl (String urlString) {AndroidHttpClient httpClient = AndroidHttpClient. newInstance (null); HttpGet get = new HttpGet (urlString); File file = null; try {HttpResponse response = httpClient.exe cute (get); if (response. getStatusLine (). getStatusCode () = STATUS_ OK) {HttpEntity entity = response. getEntity (); file = mFileHandler. createEmptyFileToDownloadDirectory (MD5Encoder. encoding (urlString); entity. writeTo (new FileOutputStream (file);} else if (response. getStatusLine (). getStatusCode () == 404) {System. out. println ("File Download: The downloaded file does not exist") ;}} catch (Exception e) {// TODO Auto-generated catch blocke. printStackTrace ();} finally {httpClient. close (); httpClient = null;} return file ;}}
The last imageview is used to load
/*** View used to load images */public class AutoloadImageView extends ImageView {public final int DEFAULTIMAGEID =-1; private Context mContext; private ImageCache mImageCache; // private FileHandler fileHandler; public AutoloadImageView (Context context) {super (context); this. mContext = context; mImageCache = ImageCache. using instance (); // fileHandler = new FileHandler (mContext);} public AutoloadImageView (Context conte Xt, AttributeSet paramAttributeSet) {super (context, paramAttributeSet); this. mContext = context; mImageCache = ImageCache. using instance (); // fileHandler = new FileHandler (mContext);} public AutoloadImageView (Context context, AttributeSet attrs, int defStyle) {super (context, attrs, defStyle); this. mContext = context; mImageCache = ImageCache. using instance (); // fileHandler = new FileHandler (mContext );}/* ** Load image * @ param drawable * image */public void loadImage (Drawable drawable) {// drawable = MainApplication. getContext (). getResources ()//. getDrawable (R. drawable. button_focused); this. setImageDrawable (drawable);}/*** load image ** @ param imageUrlString * image url */private void loadImage (final String imageUrlString) {// set tagthis. setTag (imageUrlString);/*** call the network interface to request images */ImageDownLoad imageDownLoad = new Ima GeDownLoad (mContext); imageDownLoad. downloadImageByUrl (imageUrlString, new ImageDownloadListener () {@ Overridepublic void imageDownloadFinished (Bitmap bitmap, String fileNameString) {try {if (mContext! = Null & bitmap! = Null &&! Bitmap. isRecycled () {Log. e ("kyson", "AutoloadImageView. this "+ AutoloadImageView. this); AutoloadImageView. this. setImageBitmap (bitmap);} catch (IllegalArgumentException e) {Log. I ("wenjuan", "can not draw bitmap because it is recycled") ;}@ Overridepublic void imageDownloadFinished (Drawable image, String fileNameString ){}});} /*** load image ** @ param imageUrlString * image url, * @ param drawableId default image id */pub Lic void loadImage (String imageUrlString, int drawableId) {Bitmap bitmap = getBitmapFromCache (imageUrlString); if (bitmap! = Null) {this. setImageBitmap (bitmap);} else {BitmapFactory. options opts = new BitmapFactory. options (); opts. inJustDecodeBounds = true; if (this. DEFAULTIMAGEID = drawableId) {BitmapFactory. decodeResource (getResources (), R. drawable. default_image_icon, opts);} else {BitmapFactory. decodeResource (getResources (), drawableId, opts);} opts. inSampleSize = computeSampleSize (opts,-1,480*800); opts. inJustDeco DeBounds = false; Bitmap bmp = null; try {bmp = BitmapFactory. decodeResource (getResources (), drawableId, opts); this. setImageDrawable (new BitmapDrawable (bmp);} catch (OutOfMemoryError err) {if (bmp! = Null) {bmp. recycle (); bmp = null ;}} if (imageUrlString! = Null) {// BitmapWorkerTask bitmaptask = new BitmapWorkerTask (this); // bitmaptask.exe cute (imageUrlString); this. loadImage (imageUrlString) ;}} public int computeSampleSize (BitmapFactory. options options, int minSideLength, int maxNumOfPixels) {int initialSize = values (options, minSideLength, maxNumOfPixels); int roundedSize; if (initialSize <= 8) {roundedSize = 1; while (roundedSize <I NitialSize) {roundedSize <= 1 ;}} else {roundedSize = (initialSize + 7)/8*8;} return roundedSize;} private static int computeInitialSampleSize (BitmapFactory. options options, int minSideLength, int maxNumOfPixels) {double w = options. outWidth; double h = options. outHeight; int lowerBound = (maxNumOfPixels =-1 )? 1: (int) Math. ceil (Math. sqrt (w * h/maxNumOfPixels); int upperBound = (minSideLength =-1 )? 128: (int) Math. min (Math. floor (w/minSideLength), Math. floor (h/minSideLength); if (upperBound <lowerBound) {// return the larger one when there is no overlapping zone. return lowerBound;} if (maxNumOfPixels =-1) & (minSideLength =-1) {return 1;} else if (minSideLength =-1) {return lowerBound;} else {return upperBound;} private Bitmap getBitmapFromCache (String url) {if (TextUtils. isEmpt Y (url) {return null;} return mImageCache. loadImage (url);}/*** use this Loading Method in listview ** @ param imageUrlString * @ param drawableId * @ param pos * // public void loadImage (String imageUrlString, int drawableId, int pos) {// Bitmap bitmap = getBitmapFromCache (imageUrlString); // if (bitmap! = Null) {// this. setImageBitmap (bitmap); //} else {// if (! TextUtils. isEmpty (imageUrlString) {// String subUrl = MD5Encoder. encoding (imageUrlString); // if (fileHandler. isFileExists (subUrl) // & fileHandler. getFileSize (subUrl )! = 0) {// obtain Bitmap from the SD card/File file = fileHandler. findFileByName (subUrl, // fileHandler. getImagePath (); // Log. I ("wenjuan", // "image from sd file" + file. getAbsolutePath (); // Bitmap bitmap1 = ImageUtils //. readFileToBitmapWithCompress (file //. getAbsolutePath (); // Add Bitmap to the memory cache // if (bitmap1! = Null) {// this. setImageBitmap (bitmap1); // mImageCache. cacheImage (bitmap1, imageUrlString); //} else {// Drawable drawable = null; // load the default image first // if (this. DEFAULTIMAGEID = drawableId) {// drawable = MainApplication. getContext (). getResources ()//. getDrawable (R. drawable. default_image_icon); //} else {// drawable = MainApplication. getContext (). getResources ()//. getDrawable (drawableId); //} // this. setImageDrawable (drawable); // this. loadImage (imageUrlString); //} @ Overrideprotected void onDraw (Canvas canvas) {try {super. onDraw (canvas);} catch (Exception e ){}}}
How does ListView asynchronously load images?
Asynchronous loading means that a thread is downloading the image and refresh it immediately after the download is complete.
Android requires interface operations in the main thread. Therefore, you need to call handler to notify the adapter that the data source has changed.
How to load listview images Asynchronously
Asynchronous loading is not performed in getView, and images are asynchronously loaded in Activity. Before loading, only the text data of the Adapter and empty image data are transmitted. After loading, all the data is transmitted to the Adapter, notice Adapter change