Android開發之圖片處理專題(三):利用ThreadPoolExcutor線程池實現多圖片的非同步載入

來源:互聯網
上載者:User

標籤:多線程   線程池   threadpoolexcutor   圖片非同步載入   

在上一篇專題Android開發之圖片處理專題(二):利用AsyncTask和回調介面實現圖片的非同步載入和壓縮中我們實現了listView的圖片的大量載入。今天,我們換一種方式,採用線程池的方式來實現。

我們需要準備兩個東西:

1、圖片下載任務類

2、線程池。


1、圖片下載任務類。圖片下載任務類,將需要顯示的iamgeView,線程通訊訊息管理者handler進行了封裝。當圖片下載無論成功還是失敗,handler發送對應的訊息,傳入的iamgeView顯示對應的圖片。這裡就不在應用軟引用技術,採用的bitmapManager是採用了weakHashMap來實現緩衝。關於weakHashmap,是採用了弱引用技術,其實現原理大家可以自行去查閱,這裡就不在詳細描述了。
/** * @ClassName: AsyncImageTask * @author victor_freedom ([email protected]) * @createddate 2015-2-1 下午10:58:52 * @Description: 圖片非同步載入任務 */public class AsyncImageTask implements Runnable {private final String TAG = "AsyncImageTask";// 任務標識ID,其實就是圖片緩衝路徑private String taskId;// 傳入需要設定的ImageViewprivate ImageView imageView;// 圖片下載路徑private String destUrl;// 下載失敗顯示圖片的IDprivate int failImage = -1;// 預設圖片IDprivate int defaultImage = -1;// 壓縮比例private int sampleSize = 1;// 是否設定為backgroundprivate boolean forBackground = false;private boolean canceled = false;// 訊息管理器private AsyncHandler handler = null;// 動畫private Animation animation = null;public final static int OK = 1;public final static int FAIL = 2;public final static int EXSIT = 3;private boolean forHome = false;/** * 建構函式 *  * @param imageView *            ImageView * @param url *            圖片地址 */public AsyncImageTask(ImageView imageView, String url) {this(null, imageView, url);}/** * 建構函式 *  * @param taskId *            任務id * @param imageView *            ImageView * @param url *            圖片地址 */public AsyncImageTask(String taskId, ImageView imageView, String url) {if (null == taskId || taskId.trim().length() == 0) {// 任務標識用圖片緩衝地址來標識唯一String tid = CacheName.getCachePath(imageView.getContext(), url);this.taskId = tid;} else {this.taskId = taskId;}if (null != imageView) {this.imageView = imageView;this.imageView.setTag(this.taskId);}this.destUrl = url;this.handler = new AsyncHandler();}/** * taskId *  * @return the taskId */public String getTaskId() {return taskId;}/** * @param taskId *            the taskId to set */public void setTaskId(String taskId) {this.taskId = taskId;}/** * forHome *  * @return the forHome */public boolean isForHome() {return forHome;}/** * @param forHome *            the forHome to set */public void setForHome(boolean forHome) {this.forHome = forHome;}/** * defaultImage *  * @return the defaultImage */public int getDefaultImage() {return defaultImage;}/** * @param defaultImage *            the defaultImage to set */public void setDefaultImage(int defaultImage) {this.defaultImage = defaultImage;}/** * 擷取下載失敗顯示的圖片資源id *  * @return int */public int getFailImage() {return failImage;}/** * 設定下載失敗後要顯示圖片資源id *  * @param failImage *            R.drawable.xxx */public void setFailImage(int failImage) {this.failImage = failImage;}/** * 擷取圖片的壓縮比例 *  * @return int */public int getSampleSize() {return sampleSize;}/** * 設定圖片的採樣率 *  * @param sampleSize *            最好為2的指數倍 */public void setSampleSize(int sampleSize) {if (sampleSize > 0) {this.sampleSize = sampleSize;}}/** * 下載圖片後是否設定為背景圖 *  * @return 是背景圖返回true,否則返回false */public boolean isForBackground() {return forBackground;}/** * 設定是否顯示為背景圖 *  * @param forBackground *            預設為false */public void setForBackground(boolean forBackground) {this.forBackground = forBackground;}/** * canceled *  * @return the canceled */public boolean isCanceled() {return canceled;}/** * 取消當前任務 *  * @param canceled *            the canceled to set */public void setCanceled(boolean canceled) {this.canceled = canceled;}/** * imageView *  * @return the imageView */public ImageView getImageView() {return imageView;}/** * @param imageView *            the imageView to set */public void setImageView(ImageView imageView) {this.imageView = imageView;}/** * destUrl *  * @return the destUrl */public String getDestUrl() {return destUrl;}/** * @param destUrl *            the destUrl to set */public void setDestUrl(String destUrl) {this.destUrl = destUrl;}/** * handler *  * @return the handler */public AsyncHandler getHandler() {return handler;}/** * @param handler *            the handler to set */public void setHandler(AsyncHandler handler) {this.handler = handler;}/** * animation *  * @return the animation */public Animation getAnimation() {return animation;}/** * 設定圖片下載成功後的動畫效果 *  * @param animation *            the animation to set */public void setAnimation(Animation animation) {this.animation = animation;}@Overridepublic void run() {// 拿到緩衝地址String destPath = CacheName.getCachePath(imageView.getContext(),destUrl);// 建立檔案File file = new File(destPath);Message msg = handler.obtainMessage();// 判斷該檔案是否已經下載過if (!file.exists()) {// 如果沒有,則下載if (download(destUrl, destPath + ".tmp")) {// 建立臨時快取檔案File temp = new File(destPath + ".tmp");// 按照下載檔案命名temp.renameTo(file);msg.what = OK;msg.obj = destPath;} else {// 如果下載失敗,刪除臨時檔案msg.what = FAIL;deleteTemp(destPath + ".tmp");}} else {msg.what = EXSIT;msg.obj = destPath;}if (!canceled) {handler.sendMessage(msg);} else {// 如果中斷了下載,則清除臨時檔案deleteTemp(destPath + ".tmp");}}private void deleteTemp(String path) {File file = new File(path);if (file.exists()) {file.delete();}}private boolean download(String imageUrl, String destPath) {// 刪除同名臨時檔案deleteTemp(destPath);boolean success = false;URL url = null;InputStream is = null;OutputStream out = null;HttpURLConnection conn = null;// 下載 圖片try {url = new URL(imageUrl);conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(20000);conn.setReadTimeout(5 * 60 * 1000);conn.setDoInput(true);conn.setRequestProperty("Accept-Language", "zh-cn");conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");conn.setRequestProperty("Connection", "Keep-Alive");conn.connect();if (conn.getResponseCode() == 200) {is = conn.getInputStream();int read = 0;byte[] buffer = new byte[1024];// 拿到緩衝路徑的輸入資料流out = new FileOutputStream(destPath);while ((read = is.read(buffer)) != -1) {// 寫入資料out.write(buffer, 0, read);}// 返回成功標識位success = true;} else {Log.d(TAG, "the respond code is ---> " + conn.getResponseCode());Log.d(TAG, "the url is:" + imageUrl);}} catch (MalformedURLException e) {Log.d(TAG, "MalformedURLException ---> " + e.toString());} catch (IOException e) {Log.d(TAG, "IOException ---> " + e.toString());} finally {try {if (out != null) {out.flush();out.close();}if (conn != null) {conn.disconnect();}} catch (IOException e) {Log.d(TAG, e.toString());}}return success;}// end download/** * @ClassName: AsyncHandler * @author victor_freedom ([email protected]) * @createddate 2015-2-1 下午11:16:28 * @Description: 訊息首發器 */final class AsyncHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case FAIL:doFail(msg);break;default:if (forHome) {imageView.setScaleType(ScaleType.FIT_XY);}doSuccess(msg);break;}}/** * @Title: doFail * @Description: 下載失敗後,設定指定的失敗圖片 * @param msg * @throws */private void doFail(Message msg) {if (forBackground && failImage != -1) {imageView.setBackgroundResource(failImage);} else if (!forBackground && failImage != -1) {imageView.setImageResource(failImage);}}/** * @Title: doSuccess * @Description: 下載成功後,顯示 * @param msg * @throws */private void doSuccess(Message msg) {//拿到路徑String path = (String) msg.obj;//壓縮存放圖片BitmapManager.getInstance().putBitmap(path, sampleSize);//拿到圖片Bitmap bitmap = BitmapManager.getInstance().getBitmap(path);String tag = (String) imageView.getTag();//圖片設定if ((null != bitmap) && tag == null || tag.equals(taskId)) {if (forBackground) {imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));} else if (!forBackground) {imageView.setImageBitmap(bitmap);}//是否執行動畫if (msg.what == OK && null != animation) {imageView.setAnimation(animation);animation.start();}}}}// end AsyncHandler}

2、線程池建立這裡我們採用自訂線程池的方式,有關線程池的概述,請參考: JAVA學習筆記之多線程專題(二):線程池概述
/** * @ClassName: TaskQueue * @author victor_freedom ([email protected]) * @createddate 2015-2-1 下午11:31:19 * @Description: 圖片非同步工作管理員 */public final class TaskQueue {private final static String TAG = "TaskQueue";private static TaskQueue taskQueue = null;private static ThreadPoolExecutor threadPool = null;// 線程池private final static int CORE_SIZE = 2; // 最小背景工作執行緒數量private final static int MAX_SIZE = 3; // 最大共作線程數量private final static int QUEUE_SIZE = 20; // 線程緩衝隊列容量private final static long ALIVE_TIME = 10; // 某線程允許的最大空閑時間長度private final static TimeUnit T_Unit = TimeUnit.SECONDS; // 空閑時間長度單位:秒private static BlockingQueue<Runnable> queue = null; // 線程緩衝隊列private static RejectedExecutionHandler rejectedHandler = new DiscardOldestPolicy(); // 線程池拒絕策略,當緩衝隊列滿時,工作隊列頭部的任務將被刪除//建立任務隊列及線程池private TaskQueue() {queue = new LinkedBlockingQueue<Runnable>(QUEUE_SIZE);threadPool = new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, ALIVE_TIME,T_Unit, queue, rejectedHandler);}/** * 採用單例設計 *  * @return TaskQueue */public static TaskQueue getInstance() {if (null == taskQueue) {taskQueue = new TaskQueue();}return taskQueue;}/** * 添加一個任務 *  * @param task *            AsyncImageTask */public void addTask(AsyncImageTask task) {//檢查本地是否已經存在快取檔案if (!hadLocal(task)) {boolean had = false;//判斷任務隊列裡面是否已經存在該任務for (int i = 0; i < queue.size(); i++) {AsyncImageTask t = (AsyncImageTask) queue.element();if (task.getTaskId().equals(t.getTaskId())) {had = true;Log.d(TAG, "the task id is:" + t.getTaskId());break;}}if (!had) {//如果不存在,加入執行隊列if (task.getDefaultImage() != -1) {if (!task.isForBackground()) {task.getImageView().setImageResource(task.getDefaultImage());} else {task.getImageView().setBackgroundResource(task.getDefaultImage());}}threadPool.execute(task);} else {//如果已經存在,則不做修改。等待執行if (task.getDefaultImage() != -1) {if (!task.isForBackground()) {task.getImageView().setImageResource(task.getDefaultImage());} else {task.getImageView().setBackgroundResource(task.getDefaultImage());}}}} else {//如果有緩衝,則直接拿本地的圖片來用String destPath = CacheName.getCachePath(task.getImageView().getContext(), task.getDestUrl());Message msg = new Message();msg.what = AsyncImageTask.EXSIT;msg.obj = destPath;task.getHandler().sendMessage(msg);}}/** * 檢查本地是否已經存在快取檔案 *  * @param task * @return 存在返回true,否則返回false */private boolean hadLocal(AsyncImageTask task) {String destPath = CacheName.getCachePath(task.getImageView().getContext(), task.getDestUrl());File file = new File(destPath);if (file.exists()) {if (ImageCheck.isAvailable(destPath)) {file.setLastModified(System.currentTimeMillis());return true;} else {file.delete();}}return false;}/** * 關閉所有背景工作執行緒 */public void shutDown() {threadPool.shutdown();taskQueue = null;queue = null;}}

3、adapter編寫在adpter處我們只需要這麼寫幾句代碼即可
//拿到路徑String url = Constant.PIC_BASE_URL + news.PicUrl;//建立任務AsyncImageTask task = new AsyncImageTask(holder.iv, url);//設定預設圖片和下載失敗圖片task.setDefaultImage(R.drawable.news_image_default);task.setFailImage(R.drawable.news_image_default);//加入到任務隊列中執行TaskQueue.getInstance().addTask(task);



以上就是如何使用線程池去實現圖片的非同步載入流程。相信看過此文之後大家對多線程的操作有了一定的認識。希望此文能夠協助到看到此文的人。

Android開發之圖片處理專題(三):利用ThreadPoolExcutor線程池實現多圖片的非同步載入

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.