Picasso源码解析

Posted 流云易采

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Picasso源码解析相关的知识,希望对你有一定的参考价值。

一、Picasso使用:

gradle:

compile 'com.squareup.picasso:picasso:2.5.2'

使用:

Picasso.with(this).load("http://...../photo3.jpg").into(myImg);

自定义的使用方法:

Picasso picasso = new Picasso.Builder(this)
        .memoryCache(new LruCache())       // 设置自定义的内存缓存
        .addRequestHandler(requestHandler) // 设置自定义的RequestHandler
        .defaultBitmapConfig(bitmapConfig) // 设置自定义的Bitmap Config
        .downloader(okHttpDownloader)      // 设置自定义的Downloader
        .executor(executorService)         // 设置自定义的线程池
        .requestTransformer(transformer)   // 设置自定义的Transformor
        .listener(listener)                // 添加Listener进行监听
        .build();
picasso.load(File/resId/Uri/String)
        .resize(width, height)
        .centerInside()
        .into(imageView);

二、源码分析

(一)Picasso.with进行一系列初始化

public static Picasso with(Context context) 
    if (singleton == null) 
        synchronized (Picasso.class) 
            if (singleton == null) 
                singleton = new Builder(context).build();
            
        
    
    return singleton;

简单的多线程单例模式;同时使用Buidler模式来创建Picasso实例。

1、Picasso.Builder#build:

/** Start building a new @link Picasso instance. */
public Builder(Context context) 
    if (context == null) 
        throw new IllegalArgumentException("Context must not be null.");
    
    this.context = context.getApplicationContext();


private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;
/** Create the @link Picasso instance. */
public Picasso build() 
    Context context = this.context;

    // 为这一系列变量进行默认初始化
    // Downloader执行实际的下载业务,返回Response
    if (downloader == null) 
        downloader = Utils.createDefaultDownloader(context);
    
    // 内存缓存,可以看到默认的是LruCache
    if (cache == null) 
        cache = new LruCache(context);
    
    // 线程池,执行网络请求的地方
    if (service == null) 
        service = new PicassoExecutorService();
    
    // request发送前进行处理
    if (transformer == null) 
        transformer = RequestTransformer.IDENTITY;
    
    // 用以统计
    Stats stats = new Stats(cache);
    // 进行Request以及Response的转发
    Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

    // 创建一个Picasso
    return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
            defaultBitmapConfig, indicatorsEnabled, loggingEnabled);

可以看到这里为许多重要类进行了默认初始化,后面获取图片使用到的都是这些默认的变量,当然这些变量也都可以进行自定义;
Downloader:默认实现是OkHttpDownloader,Picasso是对OkHttp的封装,它的网络请求以及对本地Cache的请求都是直接通过OkHttp来实现的;Downloader是实现本地磁盘查询,以及发起网络请求的类;
Cache:内存缓存;这里采用的LruCache,也可以自行定义。缓存的是进行网络请求后,大小等裁剪之后的图片资源。
PicassoExecutorService: Picasso之中的线程池,默认核心线程为3个,没有非核心线程;但也会根据手机当前的网络状态进行适时改变,比如WIFI状态的核心线程就为4个,而4G,3G,2G状态下的线程数分别为3,2,1个;
Stats:进行一些数据统计,比如图片下载命中率等。
RequestTransformer:用来预处理Request,必须修改域名等
Dispatcher:用以分发任务,它通过启动了一个DispatcherThread线程,然后创建一个用来处理消息的DispatcherHandler,该Handler的数据处理是在DispatcherThread中进行的。
RequestHandler: 后面将会看到,该对象用来持有一个具体的Request,所有Request最终其实都是由其对应的RequestHandler来进行处理的。

根据后面具体使用情况再做分析:

2、Picasso#load:

public RequestCreator load(String path) 
    if (path == null) 
        return new RequestCreator(this, null, 0);
    
    if (path.trim().length() == 0) 
        throw new IllegalArgumentException("Path must not be empty.");
    
    return load(Uri.parse(path));


public RequestCreator load(Uri uri) 
    return new RequestCreator(this, uri, 0);

这里创建一个RequestCreator

private final Picasso picasso;
private final Request.Builder data;

RequestCreator(Picasso picasso, Uri uri, int resourceId) 
    if (picasso.shutdown) 
        throw new IllegalStateException(
                "Picasso instance already shut down. Cannot submit new requests.");
    
    this.picasso = picasso;
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);

data对应创建一个Request的Builder,这个Request中封装了了相应的请求信息,传入了请求加载图片的URI,已经resourceId,以及默认显示图片的配置信息。
进而一般调用into将图片加载到相应的控件中。

3、RequestCreator#into:

// RequestCreator.java
public void into(ImageView target) 
    into(target, null);


public void into(ImageView target, Callback callback) 
    long started = System.nanoTime();
    // 盘算是否是在主线程,如果不是则会抛出异常
    checkMain();

    if (target == null) 
        throw new IllegalArgumentException("Target must not be null.");
    

    // 判断reqeust是否合法,即存在URI或者对应的resId,否则会取消该请求
    if (!data.hasImage()) 
        picasso.cancelRequest(target);
        if (setPlaceholder) 
            setPlaceholder(target, getPlaceholderDrawable());
        
        return;
    

    // 判断是否需要延期执行
    if (deferred) 
        // 判断是否已经设置了宽高大小
        if (data.hasSize()) 
            throw new IllegalStateException("Fit cannot be used with resize.");
        
        // 获取目标控件的宽高参数
        int width = target.getWidth();
        int height = target.getHeight();
        // 表示当前控件并未加载到界面上(宽或高为0)
        if (width == 0 || height == 0) 
            if (setPlaceholder) 
                setPlaceholder(target, getPlaceholderDrawable());
            
            // 生成DeferredRequestCreator,加入相应队列进行处理
            picasso.defer(target, new DeferredRequestCreator(this, target, callback));
            return;
        
        // 设置Request.Builder中的宽高参数大小
        data.resize(width, height);
    

    // 创建Request
    Request request = createRequest(started);
    // 获取request对应的key
    String requestKey = createKey(request);

    // 根据策略判断是够需要跳过读取MemoryCache
    if (shouldReadFromMemoryCache(memoryPolicy))  // 尝试从MemoryCache中获取Bitmap
        // 根据Requeskey来获取相应的Bitmap
        Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
        if (bitmap != null) 
            // 如果从MemoryCache中获取到相应的bitmap,则取消request
            picasso.cancelRequest(target);
            // 设置图片
            setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
            if (picasso.loggingEnabled) 
                log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
            
            // 调用回调callback的onSuccess函数
            if (callback != null) 
                callback.onSuccess();
            
            return;
        
    

    // 如果从MemoryCache获取图片失败,或者根据缓存策略直接跳过读取MemoryCache,则设置默认图片
    if (setPlaceholder) 
        setPlaceholder(target, getPlaceholderDrawable());
    

    // 创建ImageViewAction,Action里面包含了一次请求所需要的所有信息
    Action action =
            new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                    errorDrawable, requestKey, tag, callback, noFade);

    // 将action入队列
    picasso.enqueueAndSubmit(action);

整个图片加载流程和一般的三级缓存加载流程相似,下图已经介绍地很清晰:

上面主要是尝试从MemoryCache中获取对应Bitmap,如果获取失败,再创建Action,通过将Action入队列,然后从本地Cache以及网络上去获取该图片。
接着上面enqueueAndSubmit继续分析

4、Picasso#enqueueAndSubmit:

// Picasso.java
final Map<Object, Action> targetToAction;

void enqueueAndSubmit(Action action) 
    // 根据Action获取对应的Target
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) 
        // This will also check we are on the main thread.
        // 取消该target对应的以前的请求
        cancelExistingRequest(target);
        // 添加当前请求
        targetToAction.put(target, action);
    
    // 提交action
    submit(action);

targetToAction是一个Map,它存储的是Target对应的Action,以Target为相应key值。
当新添加一个action时,首先尝试取消该Action对应的Target之前的Action请求,然后将新Action添加到Map中;
然后提交action请求;

先来看下取消一个Request的函数cancelExistingReqeust,常用的cancelReqeust也是直接调用该函数实现的:

// Picasso.java
final Map<Object, Action> targetToAction;

private void cancelExistingRequest(Object target) 
    // 判断是否在主线程中进行
    checkMain();
    // 从Map中移除该target对应action
    Action action = targetToAction.remove(target);
    // 如果该Target之前存在对应的action
    if (action != null) 
        // 则调用该action的cancel函数进行取消
        action.cancel();
        // 并且调用Dispatcher进行分发cancel请求
        dispatcher.dispatchCancel(action);
    
    // 对于ImageView类型的Target
    if (target instanceof ImageView) 
        ImageView targetImageView = (ImageView) target;
        // 前面分析提到可能有需要延迟操作的请求,它是通过添加到DeferredRequestCreator来实现的,这里的需要将其cancel掉
        DeferredRequestCreator deferredRequestCreator =
                targetToDeferredRequestCreator.remove(targetImageView);
        if (deferredRequestCreator != null) 
            deferredRequestCreator.cancel();
        
    

简单的cancel逻辑:
这里依然也需要先判断事件处理是否是在主线程中进行的;
然后尝试从targetToAction中删除该Target中对应的Action,如果该Action确实存在,则调用cancel函数取消该Action,并且调用Dispatcher来分发该cancel请求;
对应ImageView类型的target,前面分析中提到当需要defer即延迟的请求时,是通过创建DeferredReqeustCreator,并添加到一个Map中来实现的,这里同理应将其删除并cancel掉;
再来看看其cancel函数:

1)Action#cancel:

// ImageViewAction.java
class ImageViewAction extends Action<ImageView> 
    Callback callback;
    @Override void cancel() 
        super.cancel();
        if (callback != null) 
            callback = null;
        
    


// Action.java
abstract class Action<T> 
    boolean cancelled;
    void cancel() 
        cancelled = true;
    

和Volley这些差不多,通过设置一个cancel标志来实现。

2) Dispatcher#dispatchCacnel

// Dispatcher.java
void dispatchCancel(Action action) 
    handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));

分发逻辑较为简单,使用Handler来实现的;
这里来具体来看下Dispatcher的定义,来确定handler的定义;Dispathcer是在创建Picasso时默认创建了一个Dispathcer实例,如下所示:

// Picasso.java
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

static final Handler HANDLER = new Handler(Looper.getMainLooper()) 
    @Override
    public void handleMessage(Message msg) 
        switch (msg.what) 
            ...
        
    

而Dispatcher中的Handler并不是这个Handler,而是又做了一个封装:

this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
// Dispatcher.java
private static class DispatcherHandler extends Handler 
    private final Dispatcher dispatcher;

    public DispatcherHandler(Looper looper, Dispatcher dispatcher) 
        super(looper);
        this.dispatcher = dispatcher;
    

    @Override public void handleMessage(final Message msg) 
        switch (msg.what) 
            case REQUEST_SUBMIT: 
                Action action = (Action) msg.obj;
                dispatcher.performSubmit(action);
                break;
            
            case REQUEST_CANCEL: 
                Action action = (Action) msg.obj;
                dispatcher.performCancel(action);
                break;
            
            .......
            default:
                Picasso.HANDLER.post(new Runnable() 
                    @Override public void run() 
                        throw new AssertionError("Unknown handler message received: " + msg.what);
                    
                );
        
    

Dispathcer的作用是通过handler将相应的操作逻辑转换到自己Looper对应线程中去实现,然后调用performCancel去具体处理该Action;
下面接着来看Dispatcher#performCancel:

// Dispatcher.java
final Map<String, BitmapHunter> hunterMap;
final Set<Object> pausedTags;
final Map<Object, Action> pausedActions;
final Map<Object, Action> failedActions;

void performCancel(Action action) 
    // 获取Action对应的key
    String key = action.getKey();
    // 根据key获取对应的BitmapHunter
    BitmapHunter hunter = hunterMap.get(key);
    // 取消执行bitmapHunter
    if (hunter != null) 
        hunter.detach(action);
        if (hunter.cancel()) 
            hunterMap.remove(key);
        
    

    // 从暂停map中删除对应action
    if (pausedTags.contains(action.getTag())) 
        pausedActions.remove(action.getTarget());
    

    // 从失败map中删除对应Action
    Action remove = failedActions.remove(action.getTarget());

逻辑较为简单,主要从各个可能存储该Action的集合中将其删除,如果已经有Bitmap在执行,则调用去cancel去处理。里面引出一个重要的类BitmapHunter,下面将会进行介绍。

如果没有从MemoryCache中获取到Bitmap(分两种情况,一直是根据policy直接跳过从MemoryCache中获取;另一种是MemoryCache中并没有对应的Bitmap缓存),这是将会来到submit(Action);

5、Picasso#submit:

// Picasso.java
void submit(Action action) 
    dispatcher.dispatchSubmit(action);

和前面类似。继续调用Dispatcher来分发submit事件,因此可以看出Dispatcher的主要作用是对Picasso(主线程)中发送的事件进行分发处理;
和dispatchCancel类似,接下来会来到performSubmit:

6、Dispatcher#performSubmit:

final Set<Object> pausedTags;
final Map<Object, Action> failedActions;
final Map<Object, Action> pausedActions;
final Map<String, BitmapHunter> hunterMap;
final ExecutorService service;

void performSubmit(Action action, boolean dismissFailed) 
    // 如果Action已经暂停
    if (pausedTags.contains(action.getTag())) 
        pausedActions.put(action.getTarget(), action);
        return;
    

    // 获取对应的BitmapHunter
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null)  // 如果对应hunter不为null,表示前面有同样的请求在进行,因此这是可以合并请求
        hunter.attach(action);
        return;
    

    // 判断是否已经关闭
    if (service.isShutdown()) 
        return;
    

    // 创建BitmapHunter
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    // 将Bitmap提交到线程池
    hunter.future = service.submit(hunter);
    // 保存Bitmap
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) 
        failedActions.remove(action.getTarget());
    

具体的工作逻辑如下图:

当遇到相同的Action时,Picasso会通过BitmapHunter的Attach来合并相同的请求,避免同一个请求重复进行;简单看下attach:

1)Bitmap#attach:

// BitmapHunter.java
void attach(Action action) 
    boolean loggingEnabled = picasso.loggingEnabled;
    Request request = action.request;

    // 如果BitmapHunter对应的action仍未设置,则直接设置action返回
    if (this.action == null) 
        this.action = action;
        return;
    

    // 创建一个保存相同Action的ArrayList
    if (actions == null) 
        actions = new ArrayList<Action>(3);
    
    actions.add(action);

    // 获得Action的优先级
    Picasso.Priority actionPriority = action.getPriority();
    // 如果新添加Action的Priority优先级高于原来的优先级,则更新该Bitmaphunter的优先级
    if (actionPriority.ordinal() > priority.ordinal()) 
        priority = actionPriority;
    

可以看到合并相同请求,则是在BitmapHunter中维护一个Action列表actions;
当新添加Action的Priority优先级高于原来的优先级,则更新该BitmapHunter的优先级大小;
这里在来关注下Picasso中的优先级问题:

/**
 * The priority of a request.
 *
 * @see RequestCreator#priority(Priority)
 */
public enum Priority 
  LOW,
  NORMAL,
  HIGH

Picasso中Priority是个枚举类,总共分为LOW,NORMAL,HIGH三个等级;而相对应地,Volley中Request的Priority则多了一个IMMEDIATE等级;顺便提下Volley中Reqeust可以根据Priority进行执行的原因是因为其实现了Comparable接口:
Volley#Reqeust:

public abstract class Request<T> implements Comparable<Request<T>> 
    public enum Priority 
        LOW,
        NORMAL,
        HIGH,
        IMMEDIATE
    

    @Override
    public int compareTo(Request<T> other) 
        Priority left = this.getPriority();
        Priority right = other.getPriority();

        // High-priority requests are "lesser" so they are sorted to the front.
        // Equal priorities are sorted by sequence number to provide FIFO ordering.
        return left == right ?
                this.mSequence - other.mSequence :
                right.ordinal() - left.ordinal();
    

2)ExecutorService:

再来看下线程池ExecutorService,它是在Picasso创建之初Builder时就创建的,默认的实现为:

// Picasso.Builder
private ExecutorService service;
if (service == null) 
    service = new PicassoExecutorService();
2.1)来看PicassoExecutorService:
// PicassoExecutorService
class PicassoExecutorService extends ThreadPoolExecutor 
    // 默认的线程池的线程数量
    private static final int DEFAULT_THREAD_COUNT = 3;

    PicassoExecutorService() 
        super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
                new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
    

Picasso提供的是个默认核心线程数为3个,没有非核心线程(最大的线程数目也是为3),任务队列为PriorityBlockingQueue的线程池;
Picasso有个有趣的地方是,它可以根据当前应用的网络状态实时调整线程池中的线程数目:

2.2)PicassoExecutorService#adjustThreadCount:
// 根据网络状况调整线程池中线程的数量
void adjustThreadCount(NetworkInfo info) 
    if (info == null || !info.isConnectedOrConnecting()) 
        setThreadCount(DEFAULT_THREAD_COUNT);
        return;
    
    switch (info.getType()) 
        case ConnectivityManager.TYPE_WIFI:
        case ConnectivityManager.TYPE_WIMAX:
        case ConnectivityManager.TYPE_ETHERNET:
            setThreadCount(4);
            break;
        case ConnectivityManager.TYPE_MOBILE:
            switch (info.getSubtype()) 
                case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
                case TelephonyManager.NETWORK_TYPE_HSPAP:
                case TelephonyManager.NETWORK_TYPE_EHRPD:
                    setThreadCount(3);
                    break;
                case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
                case TelephonyManager.NETWORK_TYPE_CDMA:
                case TelephonyManager.NETWORK_TYPE_EVDO_0:
                case TelephonyManager.NETWORK_TYPE_EVDO_A:
                case TelephonyManager.NETWORK_TYPE_EVDO_B:
                    setThreadCount(2);
                    break;
                case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
                case TelephonyManager.NETWORK_TYPE_EDGE:
                    setThreadCount(1);
                    break;
                default:
                    setThreadCount(DEFAULT_THREAD_COUNT);
            
            break;
        default:
            setThreadCount(DEFAULT_THREAD_COUNT);
    


// 线程线程数量
private void setThreadCount(int threadCount) 
    setCorePoolSize(threadCount);
    setMaximumPoolSize(threadCount);

即WIFI状态下线程数目为4,4G状态下为3,3G状态下为2,2G转态下线程池中只有一个线程;

再来看线程池中创建线程的Factory,看看Picasso的线程池中的线程有何特别之处:

2.3)PicassoThreadFactory:
static class PicassoThreadFactory implements ThreadFactory 
    @SuppressWarnings("NullableProblems")
    public Thread newThread(Runnable r) 
        return new PicassoThread(r);
    


private static class PicassoThread extends Thread 
    public PicassoThread(Runnable r) 
        super(r);
    

    @Override public void run() 
        Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
        super.run();
    

PicassoThread简单的Thread,线程优先级为后台线程;
再看service提交submit一个BitmapHunter时的操作逻辑:

2.4)PicassoExecutorService#submit:
// PicassoExecutorService
class PicassoExecutorService extends ThreadPoolExecutor 

    // 提交任务
    @Override
    public Future<?> submit(Runnable task) 
        PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
        execute(ftask);
        return ftask;
    

    // 将BitmapHunter封装成PicassoFutureTask进行execute
    private static final class PicassoFutureTask extends FutureTask<BitmapHunter>
            implements Comparable<PicassoFutureTask> 
        private final BitmapHunter hunter;

        public PicassoFutureTask(BitmapHunter hunter) 
            super(hunter, null);
            this.hunter = hunter;
        

        @Override
        public int compareTo(PicassoFutureTask other) 
            Picasso.Priority p1 = hunter.getPriority();
            Picasso.Priority p2 = other.hunter.getPriority();

            // High-priority requests are "lesser" so they are sorted to the front.
            // Equal priorities are sorted by sequence number to provide FIFO ordering.
            return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());
        
    

可以看到在submit中将BitmapHunter封装成一个FutureTask进行execute;
PicassoFutureTask可以实现对当前Runnbale运行结果的查看及控制;同期它提供了和Volley一个样的Priority一样的优先级机制。

回到前面performSubmit的执行逻辑,接下来会创建以及执行BitmapHunter,下面重点介绍BitmapHunter,它是一个任务具体执行的整体:

7、BitmapHunter:

1)Dispatcher#forReqeust:

// BitmapHunter.java
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
                               Action action) 
    // 根据Action获取对应Request
    Request request = action.getRequest();
    // 获取requestHandlers
    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();

    // Index-based loop to avoid allocating an iterator.
    //noinspection ForLoopReplaceableByForEach
    // 查找使用能够处理该Request的requesthandler,如果有则传入BitmapHandler
    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);
        
    
    // 使用默认的ERRORING_HANDLER来构造BitmapHunter
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);

先判断Picasso中是否已经设置了能够处理该Request的requestHandler,如果存在则传入BitmapHandler,如果不存在,则使用默认的ERRORING_HANDLER来构造BitmapHandler;
简单看下ERRORING_HANDLER:

// BitmapHunter.java
private static final RequestHandler ERRORING_HANDLER = new RequestHandler() 
    @Override public boolean canHandleRequest(Request data) 
        return true;
    

    @Override public Result load(Request request, int networkPolicy) throws IOException 
        throw new IllegalStateException("Unrecognized type of request: " + request);
    
;

事实上处理Request请求主要是通过reqeustHandler的load函数来进行处理的,然后将处理后的结果封装成Result,然后返回;Picasso在其构造函数中提供了一些列的RequestHandler:

List<RequestHandler> allRequestHandlers =
    new ArrayList<RequestHandler>(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);

可以看到,按照不同的Request,Picasso提供了不同的RequestHandler进行处理,比如从资源中进行加载的则使用ResourceRequestHandler,从文件中加载则调用FileRequestHandler,从网络中直接下载使用NetworkRequestHandler等等。
下面将会看到这些RequestHandler的具体处理作用;

2)BitmapHunter:

// BitmapHunter.java
class BitmapHunter implements Runnable 
    /**
     * Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since
     * this will only ever happen in background threads we help avoid excessive memory thrashing as
     * well as potential OOMs. Shamelessly stolen from Volley.
     */
    private static final Object DECODE_LOCK = new Object();
    @Override 
    public void run() 
        try 
            updateThreadName(data);
            // 主要的执行逻辑在hunt方法中
            result = hunt();

            // 通过Dispatcher来分发运行之后的结果
            if (result == null) 
                dispatcher.dispatchFailed(this);
             else 
                dispatcher.dispatchComplete(this);
            
            // 下面是进行运行过程中的一邪猎以往情况的处理
         catch (Downloader.ResponseException e) 
            if (!e.localCacheOnly || e.responseCode != 504) 
                exception = e;
            
            dispatcher.dispatchFailed(this);
         catch (NetworkRequestHandler.ContentLengthException e) 
            exception = e;
            dispatcher.dispatchRetry(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);
        
    

BitmapHunter实际上是一个Runnable,重点看其run中的执行逻辑;看到执行较为简单,都是通过hunt这个函数来实现的,获取到结果后,根据结果的成功与否,通过Dispatcher进行转发结果;
BitmapHunter中有一个成员变量较为有趣,DECODE_LOCK,这个变量在Volley中出现过,而且代码作者也说这是它从Volley中学习过来来,从名称中就可以看出,它是用来在解码图片时使用的锁,用来保证系统在同一时刻只有一个线程在解码图片;

2.1)Bitmap#hunt:
// BitmapHunter.java
final Request data;
Bitmap hunt() throws IOException 
    Bitmap bitmap = null;

    // 这里还是先尝试从MemoryCache中去获取Bitmap
    if (shouldReadFromMemoryCache(memoryPolicy)) 
        bitmap = cache.get(key);
        if (bitmap != null)  // 如果获取成功,直接返回
            stats.dispatchCacheHit(); // 统计命中信息
            loadedFrom = MEMORY;      // loadedFrom记录表示从Memory缓存中获取
            return bitmap;
        
    

    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    // 通过RequestHandler.load来处理Request获取结果
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    // 如果处理结果成功
    if (result != null) 
        loadedFrom = result.getLoadedFrom();
        exifRotation = result.getExifOrientation();

        // 从结果中获取Bitmap
        bitmap = result.getBitmap();

        // If there was no Bitmap then we need to decode it from the stream.
        // 如果Bitmap为null,则需要将Stream转化成Bitmap
        if (bitmap == null) 
            InputStream is = result.getStream();
            try 
                bitmap = decodeStream(is, data); // 将网络数据流decode成Bitmap
             finally 
                Utils.closeQuietly(is);
            
        
    

    if (bitmap != null) 
        // 更新统计信息   
        stats.dispatchBitmapDecoded(bitmap);
        // 判断是否需要进行Transformation,即进行转换
        if (data.needsTransformation() || exifRotation != 0) 
            // 这里体现了前面提到的DECODE_LOCK的作用,它保证在同一时刻,只有一个线程在执行图形变换
            synchronized (DECODE_LOCK) 
                if (data.needsMatrixTransform() || exifRotation != 0) 
                    bitmap = transformResult(data, bitmap, exifRotation);
                
                if (data.hasCustomTransformations()) 
                    bitmap = applyCustomTransformations(data.transformations, bitmap);
                
            
            if (bitmap != null) 
                stats.dispatchBitmapTransformed(bitmap);
            
        
    

    return bitmap;

hunt获取Bitmap的流程也较为清晰,首先仍然进行一次尝试从MemoryCache中进行获取;
获取失败,通过requestHandler的load函数进行加载,加载返回的结果为Result;
然后判断获取到的结果是否已经是Bitmap,如果不是,则需要将其从Stream转化成Bitmap;
然后判断是否需要Transformation;使用Transformation可以实现对获取到的图片的转化,一般的使用方式为:

Picasso.with(this).load("").transform(new Transformation() 
    @Override
    public Bitmap transform(Bitmap source) 
        // 进行图片转换
        return null;
    

    @Override
    public String key() 
        return null;
    
).into(mImageView);

使用transform方法中添加Transformation进行图片处理,相应的图片处理开源库picasso-transformations源码地址已经集成了一些常用的Transformation可以直接进行使用;
transform的主要作用是将新创建Transformation对象添加到Request的transformations这个ArrayList中;
因而判断Request是否需要进行transform的方法:

// Request.java
boolean needsTransformation() 
    return needsMatrixTransform() || hasCustomTransformations();


// 判断是否需要矩阵变换
boolean needsMatrixTransform() 
    return hasSize() || rotationDegrees != 0;


// 即将Bitmap转化成ImageView适合的尺寸
public boolean hasSize() 
    return targetWidth != 0 || targetHeight != 0;


// 判断用户是否添加了相应的Transformation
boolean hasCustomTransformations() 
    return transformations != null;

然后根据需要,对图片进行转换;
这里转换时使用到了前面所提到的DECODE_LOCK,它保证在同一时刻,只有一个线程在执行图形变换操作;
流程梳理清楚了,下面接着来看通过requestHandler接着获取Bitmap的过程,这里通过NetworkRequestHandler为例,即从网络中获取;

2.2)NetworkRequestHandler#load:
// NetWorkRequestHandler.java
class NetworkRequestHandler extends RequestHandler 
    private final Downloader downloader;
    @Override
    public RequestHandler.Result load(Request request, int networkPolicy) throws IOException 
        // 可以看到真正处理网络请求的操作时通过downloader来实现的
        Downloader.Response response = downloader.load(request.uri, request.networkPolicy);
        // 下面处理response结果
        if (response == null) 
            return null;
        
        // 判断数据是从磁盘还是网络总获取的
        Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;

        Bitmap bitmap = response.getBitmap();
        if (bitmap != null)  // 如果可以直接获取到Bitmap,则将其封装成Result,返回
            return new RequestHandler.Result(bitmap, loadedFrom);
        

        // 否则获取其输入流InputStream
        InputStream is = response.getInputStream();
        if (is == null) 
            return null;
        
        // 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.
        // 处理无效的Content长度为0的情况
        if (loadedFrom == DISK && response.getContentLength() == 0) 
            Utils.closeQuietly(is);
            // 抛出ContentLengthException,在Bitmap的run函数中会处理该异常,会调用retry进行重试
            throw new ContentLengthException("Received response with 0 content-length header.");
        
        if (loadedFrom == NETWORK && response.getContentLength() > 0) 
            stats.dispatchDownloadFinished(response.getContentLength());
        
        // 封装成Result,返回
        return new RequestHandler.Result(is, loadedFrom);
    

这里可以看到,真正处理请求的其实是Downloader这个实例,处理后返回的结果为Response;
Response中返回的结果可能有两种情况,一种是可能已经获取到了bitmap,一种可能是InputStream,这里需要分别进行处理,封装成Result,返回给BitmapHunter;
这里有个细节,当从磁盘中获取的结果Content的长度为0时,这里会抛出ContentLengthException异常,在BitmapHunter的run函数中会处理该异常,会调用retry进行重试;
downloader也是在Picasso初始化时就创建了的,如下所示:

if (downloader == null) 
  downloader = Utils.createDefaultDownloader(context);
2.3)Downloader:
// Utils.java
static Downloader createDefaultDownloader(Context context) 
    try 
        Class.forName("com.squareup.okhttp.OkHttpClient");
        return OkHttpLoaderCreator.create(context);
     catch (ClassNotFoundException ignored) 
    
    return new UrlConnectionDownloader(context);

可以看到这里提供了两种实现,一种是如果系统中已经添加了OkhttpClient依赖,则使用Class.forName加载OkHttpClient类时,会加载成功,这时使用OkHttpLoaderCreator进行创建,也就是下面的网络请求通过okHttp来实现;否则是使用android中默认提供的UrlConnection;这里一句OkHttp来进行分析;

Picasso源码解析之Lrucache算法源码解析

Picasso源码解析

Picasso源码解析

Picasso源码解析

Picasso源码解析

Picasso源码解析 一