Picasso源码完全解析(五)--图片的获取(BitmapHunter)

来源:互联网 发布:2016开淘宝店晚不晚 编辑:程序博客网 时间:2024/05/01 21:35


Picasso源码完全解析(一)--概述

Picasso源码完全解析(二)--Picasso实例的创建

Picasso源码完全解析(三)--Request和Action的创建

Picasso源码完全解析(四)--Action分发和执行

Picasso源码完全解析(五)--图片的获取(BitmapHunter)

Picasso源码完全解析(六)--请求的取消、暂停、和恢复

Picasso源码完全解析(七)-- CleanupThread 取消请求


Picasso源码完全解析(五)--图片的获取(BitmapHunter)


从上一节的分析可以知道,最终的Action是由dispatcher.performSubmit(action)执行的,看看这个方法的源码:

  void performSubmit(Action action) {    performSubmit(action, true);}void performSubmit(Action action, boolean dismissFailed) {    if (pausedTags.contains(action.getTag())) {        pausedActions.put(action.getTarget(), action);        if (action.getPicasso().loggingEnabled) {            log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),                    "because tag '" + action.getTag() + "' is paused");        }        return;    }    BitmapHunter hunter = hunterMap.get(action.getKey());    if (hunter != null) {        hunter.attach(action);        return;    }    if (service.isShutdown()) {        if (action.getPicasso().loggingEnabled) {            log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");        }        return;    }    hunter = forRequest(action.getPicasso(), this, cache, stats, action);    hunter.future = service.submit(hunter);    hunterMap.put(action.getKey(), hunter);    if (dismissFailed) {        failedActions.remove(action.getTarget());    }    if (action.getPicasso().loggingEnabled) {        log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());    }}

上面的代码的逻辑很清晰,首先根据Action的Tag判断该Action是否应该被暂停,如果是则把该Action添加到暂停的Action的Map集合里然后返回;否则在已有的hunter中查找该Action对应的Hunter,如果存在直接使用该Hunter的结果。否则,这个Action是一个新的请求,首先通过forRequest()创建该Action的BitMapHunter,然后将该Hunter提交到线程池上执行,同时将该Hunter加入Hunter Map集合。至此,我们终于看见任务被提交到线程池上了。

BitMapHunter是如何创建的----职责链模式

首先我们来看看forRequest()的源码:

static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,                               Action action) {    Request request = action.getRequest();    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();    // Index-based loop to avoid allocating an iterator.    //noinspection ForLoopReplaceableByForEach    for (int i = 0, count = requestHandlers.size(); i < count; i++) {        RequestHandler requestHandler = requestHandlers.get(i);        if (requestHandler.canHandleRequest(request)) {            return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);        }    }    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);}

我们可以看到,首先从Action里取出其对应的Request,然后从Picasso里取出RequestHandler列表,遍历这个列表,找出能够处理该Request的handler并创建BitmapHunter,如果找不到,创建默认的BitmapHunter,这是一个典型的职责链模式。

那么,职责链是如何创建的,我们看看Picasso的构造函数:

  int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);    List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);    // ResourceRequestHandler needs to be the first in the list to avoid    // forcing other RequestHandlers to perform null checks on request.uri    // to cover the (request.resourceId != 0) case.    allRequestHandlers.add(new ResourceRequestHandler(context));    if (extraRequestHandlers != null) {        allRequestHandlers.addAll(extraRequestHandlers);    }    allRequestHandlers.add(new ContactsPhotoRequestHandler(context));    allRequestHandlers.add(new MediaStoreRequestHandler(context));    allRequestHandlers.add(new ContentStreamRequestHandler(context));    allRequestHandlers.add(new AssetRequestHandler(context));    allRequestHandlers.add(new FileRequestHandler(context));    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));    requestHandlers = Collections.unmodifiableList(allRequestHandlers);

上面这段代码可以看出,首先在RequestHandler列表里加入ResourceRequestHandler,用于处理资源请求,其次加入调用者自己加入的RequestHandler,最后加入Picasso默认的RequestHandler。这样,职责链就在Picasso创建的时候建立了。同时,我们应该注意到,当我们有新的图片处理逻辑的时候,可以通过增加RequestHandler来扩展我们的需求,只需要继承RequestHandler,实现其

public abstract boolean canHandleRequest(Request data);public abstract Result load(Request request, int networkPolicy) throws IOException;

方法,同时将RequestHandler实例在Picasso初始化的时候注入进去就可以了。这也体现了面向接口编程的思想。

BitMap是如何获取的

hunter.future = service.submit(hunter);

从这段代码我们可以看到Hunter最终被提交到线程池上执行。

class BitmapHunter implements Runnable 

BitmapHunter其实是一个Runnable,是对请求任务的封装,所以,在线程池上执行的是其run()方法,源码如下:

 @Overridepublic void run() {    try {        updateThreadName(data);        if (picasso.loggingEnabled) {            log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));        }        result = hunt();        if (result == null) {            dispatcher.dispatchFailed(this);        } else {            dispatcher.dispatchComplete(this);        }    } catch (NetworkRequestHandler.ResponseException e) {        if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {            exception = e;        }        dispatcher.dispatchFailed(this);    } catch (IOException e) {        exception = e;        dispatcher.dispatchRetry(this);    } catch (OutOfMemoryError e) {        StringWriter writer = new StringWriter();        stats.createSnapshot().dump(new PrintWriter(writer));        exception = new RuntimeException(writer.toString(), e);        dispatcher.dispatchFailed(this);    } catch (Exception e) {        exception = e;        dispatcher.dispatchFailed(this);    } finally {        Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);    }}

代码很清晰,通过hunt()方法,获得Bitmap,然后将结果通过Dispatcher进行分发处理,原理如前面章节所述。

BitMap的获取主要在hunt()里,源码如下:

 Bitmap hunt() throws IOException {    Bitmap bitmap = null;    if (shouldReadFromMemoryCache(memoryPolicy)) {        bitmap = cache.get(key);        if (bitmap != null) {            stats.dispatchCacheHit();            loadedFrom = MEMORY;            if (picasso.loggingEnabled) {                log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");            }            return bitmap;        }    }    networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;    RequestHandler.Result result = requestHandler.load(data, networkPolicy);    if (result != null) {        loadedFrom = result.getLoadedFrom();        exifOrientation = result.getExifOrientation();        bitmap = result.getBitmap();        // If there was no Bitmap then we need to decode it from the stream.        if (bitmap == null) {            Source source = result.getSource();            try {                bitmap = decodeStream(source, data);            } finally {                try {                    //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.                    source.close();                } catch (IOException ignored) {                }            }        }    }    if (bitmap != null) {        if (picasso.loggingEnabled) {            log(OWNER_HUNTER, VERB_DECODED, data.logId());        }        stats.dispatchBitmapDecoded(bitmap);        if (data.needsTransformation() || exifOrientation != 0) {            synchronized (DECODE_LOCK) {                if (data.needsMatrixTransform() || exifOrientation != 0) {                    bitmap = transformResult(data, bitmap, exifOrientation);                    if (picasso.loggingEnabled) {                        log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());                    }                }                if (data.hasCustomTransformations()) {                    bitmap = applyCustomTransformations(data.transformations, bitmap);                    if (picasso.loggingEnabled) {                        log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");                    }                }            }            if (bitmap != null) {                stats.dispatchBitmapTransformed(bitmap);            }        }    }    return bitmap;}

最关键的是这句代码:

 RequestHandler.Result result = requestHandler.load(data, networkPolicy);

由此可见,Bitmap最终是由BitmapHunter的requestHandler.load()方法获取的。

Picasso默认提供了ResourceRequestHandler、ContactsPhotoRequestHandler、MediaStoreRequestHandler、ContentStreamRequestHandler、 AssetRequestHandler、FileRequestHandler、NetworkRequestHandler等7中RequestHandler,用来从不同的来源获取Bitmap,下面重点分析NetworkRequestHandler的实现。

 @Overridepublic Result load(Request request, int networkPolicy) throws IOException {    okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);    Response response = downloader.load(downloaderRequest);    ResponseBody body = response.body();    if (!response.isSuccessful()) {        body.close();        throw new ResponseException(response.code(), request.networkPolicy);    }    // Cache response is only null when the response comes fully from the network. Both completely    // cached and conditionally cached responses will have a non-null cache response.    Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;    // Sometimes response content length is zero when requests are being replayed. Haven't found    // root cause to this but retrying the request seems safe to do so.    if (loadedFrom == DISK && body.contentLength() == 0) {        body.close();        throw new ContentLengthException("Received response with 0 content-length header.");    }    if (loadedFrom == NETWORK && body.contentLength() > 0) {        stats.dispatchDownloadFinished(body.contentLength());    }    return new Result(body.source(), loadedFrom);}

上面是2.5.2最新版本,该版本已经全面使用OkHttp下载图片,磁盘缓存也是由OkHttp提供。

至此,如何获取Bitmap分析完毕。

阅读全文
0 0
原创粉丝点击