/* * Copyright (C) 2013 Peng fei Pan <sky@xiaopan.me> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.xiaopan.sketch.request; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import me.xiaopan.sketch.SLogType; import me.xiaopan.sketch.Sketch; import me.xiaopan.sketch.SketchMonitor; import me.xiaopan.sketch.cache.BitmapPool; import me.xiaopan.sketch.cache.MemoryCache; import me.xiaopan.sketch.drawable.RefBitmap; import me.xiaopan.sketch.drawable.RefBitmapDrawable; import me.xiaopan.sketch.drawable.RefDrawable; import me.xiaopan.sketch.drawable.ShapeBitmapDrawable; import me.xiaopan.sketch.drawable.SketchDrawable; import me.xiaopan.sketch.drawable.SketchGifDrawable; import me.xiaopan.sketch.util.SketchUtils; /** * 显示请求 */ public class DisplayRequest extends LoadRequest { private DisplayOptions displayOptions; private DisplayListener displayListener; private ViewInfo viewInfo; private RequestAndViewBinder requestAndViewBinder; protected DisplayResult displayResult; public DisplayRequest(Sketch sketch, DisplayInfo requestInfo, DisplayOptions displayOptions, ViewInfo viewInfo, RequestAndViewBinder requestAndViewBinder, DisplayListener displayListener, DownloadProgressListener downloadProgressListener) { super(sketch, requestInfo, displayOptions, null, downloadProgressListener); this.viewInfo = viewInfo; this.displayOptions = displayOptions; this.requestAndViewBinder = requestAndViewBinder; this.displayListener = displayListener; this.requestAndViewBinder.setDisplayRequest(this); setLogName("DisplayRequest"); } /** * 获取内存缓存key */ public String getMemoryCacheKey() { return ((DisplayInfo) getInfo()).getMemoryCacheKey(); } /** * 获取View信息 */ public ViewInfo getViewInfo() { return viewInfo; } /** * 获取显示选项 */ @Override public DisplayOptions getOptions() { return displayOptions; } @Override public boolean isCanceled() { if (super.isCanceled()) { return true; } // 绑定关系已经断了就直接取消请求 if (requestAndViewBinder.isBroken()) { canceled(CancelCause.BIND_DISCONNECT); return true; } return false; } @Override public void error(ErrorCause errorCause) { if (displayListener != null || displayOptions.getErrorImage() != null) { setErrorCause(errorCause); postRunError(); } else { super.error(errorCause); } } @Override public void canceled(CancelCause cancelCause) { super.canceled(cancelCause); if (displayListener != null) { postRunCanceled(); } } @Override protected void postRunError() { setStatus(Status.WAIT_DISPLAY); super.postRunError(); } @Override protected void postRunCompleted() { setStatus(Status.WAIT_DISPLAY); super.postRunCompleted(); } @Override protected void runLoad() { if (isCanceled()) { if (SLogType.REQUEST.isEnabled()) { printLogW("canceled", "runLoad", "display request just started"); } return; } // 先检查内存缓存,检查的时候要先上锁 boolean finished = false; if (!displayOptions.isCacheInDiskDisabled()) { setStatus(Status.GET_MEMORY_CACHE_EDIT_LOCK); finished = checkMemoryCache(); } if (!finished) { super.runLoad(); } } private boolean checkMemoryCache() { if (isCanceled()) { if (SLogType.REQUEST.isEnabled()) { printLogW("canceled", "runDownload", "get memory cache edit lock after"); } return true; } // 检查内存缓存 if (!displayOptions.isCacheInMemoryDisabled()) { setStatus(Status.CHECK_MEMORY_CACHE); MemoryCache memoryCache = getConfiguration().getMemoryCache(); RefBitmap cachedRefBitmap = memoryCache.get(getMemoryCacheKey()); if (cachedRefBitmap != null) { if (!cachedRefBitmap.isRecycled()) { if (SLogType.REQUEST.isEnabled()) { printLogI("from memory get drawable", "runLoad", "bitmap=" + cachedRefBitmap.getInfo()); } // 立马标记等待使用,防止被挤出去回收掉 cachedRefBitmap.setIsWaitingUse(getLogName() + ":waitingUse:fromMemory", true); Drawable drawable = new RefBitmapDrawable(cachedRefBitmap); displayResult = new DisplayResult(drawable, ImageFrom.MEMORY_CACHE, cachedRefBitmap.getAttrs()); displayCompleted(); return true; } else { memoryCache.remove(getMemoryCacheKey()); if (SLogType.REQUEST.isEnabled()) { printLogE("memory cache drawable recycled", "runLoad", "bitmap=" + cachedRefBitmap.getInfo()); } } } } return false; } @Override protected void loadCompleted() { LoadResult loadResult = getLoadResult(); if (loadResult != null && loadResult.getBitmap() != null) { Bitmap bitmap = loadResult.getBitmap(); if (bitmap.isRecycled()) { if (SLogType.REQUEST.isEnabled()) { printLogE("decode failed", "loadCompleted", "bitmap recycled", "bitmapInfo=", SketchUtils.makeImageInfo(null, bitmap, loadResult.getImageAttrs().getMimeType()), loadResult.getImageFrom()); } error(ErrorCause.BITMAP_RECYCLED); return; } BitmapPool bitmapPool = getConfiguration().getBitmapPool(); RefBitmap refBitmap = new RefBitmap(bitmap, getKey(), getUri(), loadResult.getImageAttrs(), bitmapPool); // 立马标记等待使用,防止刚放入内存缓存就被挤出去回收掉 refBitmap.setIsWaitingUse(getLogName() + ":waitingUse:new", true); // 放入内存缓存中 if (!displayOptions.isCacheInMemoryDisabled() && getMemoryCacheKey() != null) { getConfiguration().getMemoryCache().put(getMemoryCacheKey(), refBitmap); } Drawable drawable = new RefBitmapDrawable(refBitmap); displayResult = new DisplayResult(drawable, loadResult.getImageFrom(), loadResult.getImageAttrs()); displayCompleted(); } else if (loadResult != null && loadResult.getGifDrawable() != null) { SketchGifDrawable gifDrawable = loadResult.getGifDrawable(); if (gifDrawable.isRecycled()) { if (SLogType.REQUEST.isEnabled()) { printLogE("decode failed", "loadCompleted", "gif drawable recycled", "gifInfo=", gifDrawable.getInfo()); } error(ErrorCause.GIF_DRAWABLE_RECYCLED); return; } // GifDrawable不能放入内存缓存中,因为GifDrawable需要依赖Callback才能播放, // 如果缓存的话就会出现一个GifDrawable被显示在多个ImageView上的情况,这时候就只有最后一个能正常播放 displayResult = new DisplayResult((Drawable) gifDrawable, loadResult.getImageFrom(), loadResult.getImageAttrs()); displayCompleted(); } else { if (SLogType.REQUEST.isEnabled()) { printLogE("are all null", "loadCompleted"); } error(ErrorCause.DECODE_FAIL); } } protected void displayCompleted() { postRunCompleted(); } @Override protected void runCompletedInMainThread() { Drawable drawable = displayResult.getDrawable(); if (drawable == null) { if (SLogType.REQUEST.isEnabled()) { printLogD("completedDrawable is null", "runCompletedInMainThread"); } return; } displayImage(drawable); // 使用完毕更新等待使用的引用计数 if (drawable instanceof RefDrawable) { ((RefDrawable) drawable).setIsWaitingUse(getLogName() + ":waitingUse:finish", false); } } private void displayImage(Drawable drawable) { if (isCanceled()) { if (SLogType.REQUEST.isEnabled()) { printLogW("canceled", "runCompletedInMainThread"); } return; } // 过滤可能已回收的图片 if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; if (bitmapDrawable.getBitmap().isRecycled()) { // 这里应该不会再出问题了 SketchMonitor sketchMonitor = getConfiguration().getMonitor(); sketchMonitor.onBitmapRecycledOnDisplay(this, drawable instanceof RefDrawable ? (RefDrawable) drawable : null); // 图片不可用 printLogD("image display exception", "bitmap recycled", ((SketchDrawable) drawable).getInfo(), displayResult.getImageFrom()); runErrorInMainThread(); return; } } // 显示图片 if ((displayOptions.getShapeSize() != null || displayOptions.getImageShaper() != null) && drawable instanceof BitmapDrawable) { drawable = new ShapeBitmapDrawable(getConfiguration().getContext(), (BitmapDrawable) drawable, displayOptions.getShapeSize(), displayOptions.getImageShaper()); } ImageViewInterface viewInterface = requestAndViewBinder.getImageViewInterface(); if (SLogType.REQUEST.isEnabled()) { String drawableInfo = "unknown"; if (drawable instanceof RefDrawable) { drawableInfo = ((RefDrawable) drawable).getInfo(); } printLogI("image display completed", "runCompletedInMainThread", displayResult.getImageFrom().name(), drawableInfo, "viewHashCode=" + Integer.toHexString(viewInterface.hashCode())); } displayOptions.getImageDisplayer().display(viewInterface, drawable); setStatus(Status.COMPLETED); if (displayListener != null) { displayListener.onCompleted(displayResult.getImageFrom(), displayResult.getImageAttrs().getMimeType()); } } @Override protected void runErrorInMainThread() { if (isCanceled()) { if (SLogType.REQUEST.isEnabled()) { printLogW("canceled", "runErrorInMainThread"); } return; } setStatus(Status.FAILED); // 显示失败图片 if (displayOptions.getErrorImage() != null) { Drawable errorDrawable = displayOptions.getErrorImage().getDrawable(getContext(), requestAndViewBinder.getImageViewInterface(), displayOptions); displayOptions.getImageDisplayer().display(requestAndViewBinder.getImageViewInterface(), errorDrawable); } else { if (SLogType.REQUEST.isEnabled()) { printLogW("failedDrawable is null", "runErrorInMainThread"); } } if (displayListener != null) { displayListener.onError(getErrorCause()); } } @Override protected void runCanceledInMainThread() { if (displayListener != null) { displayListener.onCanceled(getCancelCause()); } } }