在Android的應用中實現(xiàn)網(wǎng)絡圖片異步加載的方法
前言
其實很幸運,入職一周之后就能跟著兩個師兄做android開發(fā),師兄都是大神,身為小白的我只能多多學習,多多努力。最近一段時間都忙的沒機會總結,今天剛完成了android客戶端圖片異步加載的類,這里記錄一下(ps:其實我這里都是參考網(wǎng)上開源實現(xiàn))
原理
在ListView或者GridView中加載圖片的原理基本都是一樣的:
先從內(nèi)存緩存中獲取,取到則返回,取不到進行下一步
從文件緩存中獲取,取到則返回并更新到內(nèi)存緩存,取不到則進行進行下一步
從網(wǎng)絡上下載圖片,并更新內(nèi)存緩存和文件緩存
流程圖如下:
同時,要注意線程的數(shù)量。一般在listview中加載圖片,大家都是開啟新的線程去加載,但是當快速滑動時,很容易造成OOM,因此需要控制線程數(shù)量。我們可以通過線程池控制線程的數(shù)量,具體線程池的大小還需要根據(jù)處理器的情況和業(yè)務情況自行判斷
建立線程池的方法如下:
ExecutorService executorService = Executors.newFixedThreadPool(5); // 5是可變的
文件緩存類
import java.io.File; import android.content.Context; public class FileCache { private static final String DIR_NAME = "your_dir"; private File cacheDir; public FileCache(Context context) { // Find the directory to save cached images if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) { cacheDir = new File( android.os.Environment.getExternalStorageDirectory(), DIR_NAME); } else { cacheDir = context.getCacheDir(); } if (!cacheDir.exists()) { cacheDir.mkdirs(); } } public File getFile(String url) { // Identify images by url's hash code String filename = String.valueOf(url.hashCode()); File f = new File(cacheDir, filename); return f; } public void clear() { File[] files = cacheDir.listFiles(); if (files == null) { return; } else { for (File f : files) { f.delete(); } } } }
內(nèi)存緩存類
這里使用了軟引用,Map<String, SoftReference<Bitmap>> cache,可以google一下軟引用的機制,簡單的說:實現(xiàn)了map,同時當內(nèi)存緊張時可以被回收,不會造成內(nèi)存泄露
import java.lang.ref.SoftReference; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import android.graphics.Bitmap; public class MemoryCache { private Map<String, SoftReference<Bitmap>> cache = Collections .synchronizedMap(new LinkedHashMap<String, SoftReference<Bitmap>>( 10, 1.5f, true)); public 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(); } }
圖片加載類
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.widget.ImageView; public class ImageLoader { /** * Network time out */ private static final int TIME_OUT = 30000; /** * Default picture resource */ private static final int DEFAULT_BG = R.drawable.plate_list_head_bg; /** * Thread pool number */ private static final int THREAD_NUM = 5; /** * Memory image cache */ MemoryCache memoryCache = new MemoryCache(); /** * File image cache */ FileCache fileCache; /** * Judge image view if it is reuse */ private Map<ImageView, String> imageViews = Collections .synchronizedMap(new WeakHashMap<ImageView, String>()); /** * Thread pool */ ExecutorService executorService; /** * Handler to display images in UI thread */ Handler handler = new Handler(); public ImageLoader(Context context) { fileCache = new FileCache(context); executorService = Executors.newFixedThreadPool(THREAD_NUM); } public void disPlayImage(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap = memoryCache.get(url); if (bitmap != null) { // Display image from Memory cache imageView.setImageBitmap(bitmap); } else { // Display image from File cache or Network queuePhoto(url, imageView); } } private void queuePhoto(String url, ImageView imageView) { PhotoToLoad photoToLoad = new PhotoToLoad(url, imageView); executorService.submit(new PhotosLoader(photoToLoad)); } private Bitmap getBitmap(String url) { File f = fileCache.getFile(url); // From File cache Bitmap bmp = decodeFile(f); if (bmp != null) { return bmp; } // From Network try { Bitmap bitmap = null; URL imageUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) imageUrl .openConnection(); conn.setConnectTimeout(TIME_OUT); conn.setReadTimeout(TIME_OUT); conn.setInstanceFollowRedirects(true); InputStream is = conn.getInputStream(); OutputStream os = new FileOutputStream(f); copyStream(is, os); os.close(); conn.disconnect(); bitmap = decodeFile(f); return bitmap; } catch (Throwable ex) { if (ex instanceof OutOfMemoryError) { clearCache(); } return null; } } private void copyStream(InputStream is, OutputStream os) { int buffer_size = 1024; try { byte[] bytes = new byte[buffer_size]; while (true) { int count = is.read(bytes, 0, buffer_size); if (count == -1) { break; } os.write(bytes, 0, count); } } catch (Exception e) { } } private Bitmap decodeFile(File f) { try { // TODO:Compress image size FileInputStream fileInputStream = new FileInputStream(f); Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream); return bitmap; } catch (FileNotFoundException e) { return null; } } private void clearCache() { memoryCache.clear(); fileCache.clear(); } /** * Task for the queue * * @author zhengyi.wzy * */ private class PhotoToLoad { public String url; public ImageView imageView; public PhotoToLoad(String url, ImageView imageView) { this.url = url; this.imageView = imageView; } } /** * Asynchronous to load picture * * @author zhengyi.wzy * */ class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; public PhotosLoader(PhotoToLoad photoToLoad) { this.photoToLoad = photoToLoad; } private boolean imageViewReused(PhotoToLoad photoToLoad) { String tag = imageViews.get(photoToLoad.imageView); if (tag == null || !tag.equals(photoToLoad.url)) { return true; } return false; } @Override public void run() { // Abort current thread if Image View reused if (imageViewReused(photoToLoad)) { return; } Bitmap bitmap = getBitmap(photoToLoad.url); // Update Memory memoryCache.put(photoToLoad.url, bitmap); if (imageViewReused(photoToLoad)) { return; } // Don't change UI in children thread BitmapDisplayer bd = new BitmapDisplayer(bitmap, photoToLoad); handler.post(bd); } class BitmapDisplayer implements Runnable { Bitmap bitmap; PhotoToLoad photoToLoad; public BitmapDisplayer(Bitmap bitmap, PhotoToLoad photoToLoad) { this.bitmap = bitmap; this.photoToLoad = photoToLoad; } @Override public void run() { if (imageViewReused(photoToLoad)) { return; } if (bitmap != null) { photoToLoad.imageView.setImageBitmap(bitmap); } else { photoToLoad.imageView.setImageResource(DEFAULT_BG); } } } } }
調(diào)用方法
ImageLoader imageLoader = new ImageLoader(context); imageLoader.disPlayImage(imageUrl, imageView);
相關文章
spring?boot之使用spring?data?jpa的自定義sql方式
這篇文章主要介紹了spring?boot之使用spring?data?jpa的自定義sql方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12SpringBoot實現(xiàn)HTTP調(diào)用的七種方式總結
小編在工作中,遇到一些需要調(diào)用三方接口的任務,就需要用到 HTTP 調(diào)用工具,這里,我總結了一下 實現(xiàn) HTTP 調(diào)用的方式,共有 7 種(后續(xù)會繼續(xù)新增),需要的朋友可以參考下2023-09-09Intellij?IDEA創(chuàng)建web項目的超詳細步驟記錄
如果剛開始接觸IDEA,或者之前使用的是eclipse/myEclipse的話,即使是創(chuàng)建一個JAVA WEB項目,估計也讓很多人費了好幾個小時,下面這篇文章主要給大家介紹了關于Intellij?IDEA創(chuàng)建web項目的超詳細步驟,需要的朋友可以參考下2022-08-08Intellij IDEA實現(xiàn)SpringBoot項目多端口啟動的兩種方法
有時候使用springboot項目時遇到這樣一種情況,用一個項目需要復制很多遍進行測試,除了端口號不同以外,沒有任何不同。遇到這種情況怎么辦呢?這時候可以使用Intellij IDEA解決2018-06-06聊聊Springboot2.x的session和cookie有效期
這篇文章主要介紹了Springboot2.x的session和cookie有效期,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09