編輯:關於Android編程
加載並展示圖片任務。
顧名思義,就是先要把圖片加載到內存中來,然後再view中顯示。
源碼:
/** * Presents load'n'display image task. Used to load image from Internet or file system, decode it to {@link Bitmap}, and * display it in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} using {@link DisplayBitmapTask}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageLoaderConfiguration * @see ImageLoadingInfo * @since 1.3.1 */ final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener { private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]";//這些log的方式方法,我們可以學習學習吆! private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]"; private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]"; private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]"; private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]"; private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]"; private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]"; private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]"; private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]"; private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]"; private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]"; private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]"; private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]"; private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]"; private static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]"; private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]"; private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]"; private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]"; private final ImageLoaderEngine engine; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; // Helper references private final ImageLoaderConfiguration configuration; private final ImageDownloader downloader; private final ImageDownloader networkDeniedDownloader; private final ImageDownloader slowNetworkDownloader; private final ImageDecoder decoder; final String uri; private final String memoryCacheKey; final ImageAware imageAware; private final ImageSize targetSize; final DisplayImageOptions options; final ImageLoadingListener listener; final ImageLoadingProgressListener progressListener; private final boolean syncLoading; // State vars private LoadedFrom loadedFrom = LoadedFrom.NETWORK; public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) { this.engine = engine; this.imageLoadingInfo = imageLoadingInfo; this.handler = handler; configuration = engine.configuration; downloader = configuration.downloader; networkDeniedDownloader = configuration.networkDeniedDownloader; slowNetworkDownloader = configuration.slowNetworkDownloader; decoder = configuration.decoder; uri = imageLoadingInfo.uri; memoryCacheKey = imageLoadingInfo.memoryCacheKey; imageAware = imageLoadingInfo.imageAware; targetSize = imageLoadingInfo.targetSize; options = imageLoadingInfo.options; listener = imageLoadingInfo.listener; progressListener = imageLoadingInfo.progressListener; syncLoading = options.isSyncLoading(); } @Override public void run() { if (waitIfPaused()) return;//處理器暫停了 if (delayIfNeed()) return;//任務中斷(推遲)了 ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;//獲取任務鎖 L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey); if (loadFromUriLock.isLocked()) { L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey); } loadFromUriLock.lock();//鎖定 Bitmap bmp; try { checkTaskNotActual();//檢查任務是否有效,如果view被回收,或被重用,則拋出異常 bmp = configuration.memoryCache.get(memoryCacheKey);//從內存中獲取圖片 if (bmp == null || bmp.isRecycled()) { bmp = tryLoadBitmap();//重新加載 if (bmp == null) return; // listener callback already was fired checkTaskNotActual();//再次檢查任務是否有效 checkTaskInterrupted();//當前線程是否被中斷 if (options.shouldPreProcess()) {//是否需要預處理 L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey); bmp = options.getPreProcessor().process(bmp); if (bmp == null) { L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey); } } if (bmp != null && options.isCacheInMemory()) {//放到內存緩存中 L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); configuration.memoryCache.put(memoryCacheKey, bmp); } } else { loadedFrom = LoadedFrom.MEMORY_CACHE; L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey); } if (bmp != null && options.shouldPostProcess()) {//是否需要後處理 L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey); bmp = options.getPostProcessor().process(bmp); if (bmp == null) { L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey); } } checkTaskNotActual();//檢查任務是否有效 checkTaskInterrupted();//檢查線程是否中斷 } catch (TaskCancelledException e) { fireCancelEvent();//異常處理的策略,好好學習了 return; } finally { loadFromUriLock.unlock();//是否鎖 } DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom); runTask(displayBitmapTask, syncLoading, handler, engine);//重新構造一個展示任務處理display } /** @return true - if task should be interrupted; false - otherwise */ private boolean waitIfPaused() { AtomicBoolean pause = engine.getPause(); if (pause.get()) {//如果處理器pause synchronized (engine.getPauseLock()) {//獲取鎖 if (pause.get()) {//如果處理器pause L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey); try { engine.getPauseLock().wait();//等待信號量 } catch (InterruptedException e) {//等待過程中中斷了 L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey); } } } return isTaskNotActual();//判斷任務是否還繼續有效 } /** @return true - if task should be interrupted; false - otherwise */ private boolean delayIfNeed() { if (options.shouldDelayBeforeLoading()) { L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey); try { Thread.sleep(options.getDelayBeforeLoading());//等待 } catch (InterruptedException e) {//等待過程中被中斷 L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } return isTaskNotActual();//判斷任務是否還繼續有效 } return false; } private Bitmap tryLoadBitmap() throws TaskCancelledException {//加載圖片的任務主體 Bitmap bitmap = null; try { File imageFile = configuration.diskCache.get(uri);//先在硬件緩存中獲取 if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {//硬件中存在 L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); loadedFrom = LoadedFrom.DISC_CACHE; checkTaskNotActual(); bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));//解析圖片 } if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {//從網絡(URI)獲取 L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey); loadedFrom = LoadedFrom.NETWORK; String imageUriForDecoding = uri; if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {//需要緩存在硬件上,並且嘗試從網絡上下載圖片到硬件上緩存 imageFile = configuration.diskCache.get(uri); if (imageFile != null) { imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath()); } } checkTaskNotActual(); bitmap = decodeImage(imageUriForDecoding); if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { fireFailEvent(FailType.DECODING_ERROR, null); } } } catch (IllegalStateException e) { fireFailEvent(FailType.NETWORK_DENIED, null); } catch (TaskCancelledException e) { throw e; } catch (IOException e) { L.e(e); fireFailEvent(FailType.IO_ERROR, e); } catch (OutOfMemoryError e) { L.e(e); fireFailEvent(FailType.OUT_OF_MEMORY, e); } catch (Throwable e) { L.e(e); fireFailEvent(FailType.UNKNOWN, e); } return bitmap; } private Bitmap decodeImage(String imageUri) throws IOException {//解析圖片 ViewScaleType viewScaleType = imageAware.getScaleType(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType, getDownloader(), options); return decoder.decode(decodingInfo); } /** @return true - if image was downloaded successfully; false - otherwise */ private boolean tryCacheImageOnDisk() throws TaskCancelledException {//加載圖片到硬件 L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try { loaded = downloadImage(); if (loaded) { int width = configuration.maxImageWidthForDiskCache; int height = configuration.maxImageHeightForDiskCache; if (width > 0 || height > 0) { L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey); resizeAndSaveImage(width, height); // TODO : process boolean result } } } catch (IOException e) { L.e(e); loaded = false; } return loaded; } private boolean downloadImage() throws IOException {//加載圖片到硬件 InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); if (is == null) { L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey); return false; } else { try { return configuration.diskCache.save(uri, is, this); } finally { IoUtils.closeSilently(is); } } } /** Decodes image file into Bitmap, resize it and save it back */ private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException { // Decode image file, compress and re-save it boolean saved = false; File targetFile = configuration.diskCache.get(uri); if (targetFile != null && targetFile.exists()) { ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight); DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options) .imageScaleType(ImageScaleType.IN_SAMPLE_INT).build(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE, getDownloader(), specialOptions); Bitmap bmp = decoder.decode(decodingInfo); if (bmp != null && configuration.processorForDiskCache != null) { L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey); bmp = configuration.processorForDiskCache.process(bmp); if (bmp == null) { L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey); } } if (bmp != null) { saved = configuration.diskCache.save(uri, bmp); bmp.recycle(); } } return saved; } @Override public boolean onBytesCopied(int current, int total) { return syncLoading || fireProgressEvent(current, total); } /** @return true - if loading should be continued; false - if loading should be interrupted */ private boolean fireProgressEvent(final int current, final int total) {//回調處理progress,在Handler中運行新的runnable if (isTaskInterrupted() || isTaskNotActual()) return false; if (progressListener != null) { Runnable r = new Runnable() { @Override public void run() { progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total); } }; runTask(r, false, handler, engine); } return true; } private void fireFailEvent(final FailType failType, final Throwable failCause) {//回調處理failed,在Handler中運行新的runable if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return; Runnable r = new Runnable() { @Override public void run() { if (options.shouldShowImageOnFail()) { imageAware.setImageDrawable(options.getImageOnFail(configuration.resources)); } listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause)); } }; runTask(r, false, handler, engine); } private void fireCancelEvent() { if (syncLoading || isTaskInterrupted()) return; Runnable r = new Runnable() { @Override public void run() { listener.onLoadingCancelled(uri, imageAware.getWrappedView()); } }; runTask(r, false, handler, engine); } private ImageDownloader getDownloader() { ImageDownloader d; if (engine.isNetworkDenied()) { d = networkDeniedDownloader; } else if (engine.isSlowNetwork()) { d = slowNetworkDownloader; } else { d = downloader; } return d; } /** * @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of * this task doesn't match to image URI which is actual for current ImageAware at * this moment) */ private void checkTaskNotActual() throws TaskCancelledException { checkViewCollected(); checkViewReused(); } /** * @return true - if task is not actual (target ImageAware is collected by GC or the image URI of this task * doesn't match to image URI which is actual for current ImageAware at this moment)); false - otherwise */ private boolean isTaskNotActual() {//任務是否還繼續有效 return isViewCollected() || isViewReused(); } /** @throws TaskCancelledException if target ImageAware is collected */ private void checkViewCollected() throws TaskCancelledException { if (isViewCollected()) { throw new TaskCancelledException(); } } /** @return true - if target ImageAware is collected by GC; false - otherwise */ private boolean isViewCollected() { if (imageAware.isCollected()) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey); return true; } return false; } /** @throws TaskCancelledException if target ImageAware is collected by GC */ private void checkViewReused() throws TaskCancelledException { if (isViewReused()) { throw new TaskCancelledException(); } } /** @return true - if current ImageAware is reused for displaying another image; false - otherwise */ private boolean isViewReused() { String currentCacheKey = engine.getLoadingUriForView(imageAware); // Check whether memory cache key (image URI) for current ImageAware is actual. // If ImageAware is reused for another task then current task should be cancelled. boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey); if (imageAwareWasReused) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey); return true; } return false; } /** @throws TaskCancelledException if current task was interrupted */ private void checkTaskInterrupted() throws TaskCancelledException { if (isTaskInterrupted()) { throw new TaskCancelledException(); } } /** @return true - if current task was interrupted; false - otherwise */ private boolean isTaskInterrupted() { if (Thread.interrupted()) { L.d(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } return false; } String getLoadingUri() { return uri; } static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) { if (sync) { r.run(); } else if (handler == null) { engine.fireCallback(r); } else { handler.post(r); } } /** * Exceptions for case when task is cancelled (thread is interrupted, image view is reused for another task, view is * collected by GC). * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.1 */ class TaskCancelledException extends Exception { } }
本文實例講述了Android編程中TextView寬度過大導致Drawable無法居中問題解決方法。分享給大家供大家參考,具體如下:在做項目的時候,很多時候我們都要用到文
Android設備之間可以除了通過wifi熱點共享上網,還可以通過藍牙共享上網,後面這個功能很少人使用,但適合某台設備沒有wifi卻有藍牙的情況。一、設置WT19i,系統
子曰:溫故而知新,可以為師矣。《論語》學習技術也一樣,對於技術文檔或者經典的技術書籍來說,指望看一遍就完全掌握,那基本不大可能,所以我們需要經常回過頭再仔細研讀幾遍,以領
概述:SurfaceView是Android中極為重要的繪圖容器,SurfaceView的圖像繪制是放在主線程之外的另一個線程中完成的。除了繪圖,SurfaceView還