美文网首页
Picasso 源码解析

Picasso 源码解析

作者: 憨人_Vivam | 来源:发表于2018-12-16 17:16 被阅读0次

    概述

    Picasso是大名鼎鼎的Square公司提供的一个适用于Android的强大的图片下载缓存库。

    简单使用

    Picasso.get()
        .load(url)
        .resize(50, 50)
        .centerCrop()
        .into(imageView)
    

    流程

    先简单总结下Picasso加载图片的流程,后面再详细解析:

    1. 通过Picasso#get()方法创建一个Picasso的实例,它是用单例模式+Builder模式实现的。使用默认方式创建Picasso实例时,它会创建默认的缓存、下载器、线程池、监控器、图片处理器集合等;
    2. 通过load(uri)方法将图片的uri传入,创建RequestCreator实例;
    3. 调用RequestCreator#into()方法,它会先从缓存中检索是否有该图片,有的话直接返回;
    4. 缓存中没有,则创建请求任务Action对象,并调用Picasso#enqueueAndSubmit(action)方法提交请求任务,而 Picasso会调用Dispatcher#dispatchSubmit(action)方法,由Dispatcher去处理;
    5. Dispatcher维护了一个子线程和消息队列,在主线程发送消息到子线程的消息队列,然后在子线程中调用Dispatcher#performSubmit(action)方法处理请求;
    6. 处理请求时,首先需要构建BitmapHunter对象,它继承自Runnable,可被线程池执行,构建时会选用合适的RequestHandler来处理请求,网络请求会使用NetworkRequestHandler,最后将BitmapHunter对象扔到线程池中,等待执行;
    7. BitmapHunter执行时会再次检索缓存查看是否有请求的图片,有就返回图片;没有再通过RequestHandler来获取图片;
    8. 获取到请求结果后,Dispatcher会对请求做批处理,延迟200ms再将这一批结果通过mainThreadHandler发送给主线程;
    9. Picasso在主线程接收到消息后,会遍历结果列表,一一调用Action对象的complete()方法将图片显示在ImageView上;如果请求失败则调用的是Action#error(exception)方法。

    源码解析

    本文使用的是2.71828版本的代码。

    Picasso

    Picasso是图片下载、变换和缓存管理器。

    初始化

    我们在使用的时候,都需要通过Picasso.get()方法来获取一个Picasso对象,很容易可以知道它是单例的存在,这里使用了单例模式中的双重锁模式:

    static volatile Picasso singleton = null;
    public static Picasso get() {
        if (singleton == null) {
            synchronized (Picasso.this) {
                if (singleton == null) {
                    if (PicassoProvider.context == null) {
                        throw new IllegalStateException("context == null");
                    }
                    singleton = new Builder(PicassoProvider.context).build();
                }
            }
        }
        return singleton;
    }
    

    构建Picasso对象时,使用的是PicassoProvidercontext对象,PicassoProvider继承自ContentProvider。构建时,采用了Builder模式,我们来看下Builderbuild()方法做了哪些事情。

    public Picasso build() {
        Context context = this.context;
        // 创建默认的下载器,默认使用OkHttp3
        if (downloader == null) {
            = new OkHttp3Downloader(context);
        }
        // 创建默认的缓存
        if (cache == null) {
            cache = new LruCache(context);
        }
        // 创建默认的线程池,PicassoExecutorService继承自ThreadPoolExecutor
        if (service == null) {
            service = new PicassoExecutorService();
        }
        // 创建默认的Transformer
        if (transformer == null) {
            transformer = RequestTransformer.IDENTITY;
        }
        // 创建默认的监控器
        Stats stats = new Stats(cache);
        // 创建分发器
        Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
        return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
                defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
    

    可以看到build()方法就是创建一堆基本配置:

    • 创建默认的下载器,默认使用OkHttp3 ,可通过.downloader(downloader)指定下载器。
    • 创建默认的缓存,默认使用LruCache, 可通过.memoryCache(memoryCache)指定缓存。
    • 创建默认的线程池,PicassoExecutorService,继承自ThreadPoolExecutor,默认有3个工作线程,可通过.executor(executorService)指定线程池。
    • 创建默认的RequestTransformerRequestTransformer是个接口,它的唯一方法transformRequest(request)将在请求提交之前被调用,默认实现不做任何事,直接返回请求,可自行实现该接口并使用。
    • 创建默认的监控器,用来统计缓存命中率、下载时长等,不支持自定义。
    • 创建分发器,不支持自定义
    • 创建默认的处理器集合,即RequestHandler,用于处理不同的加载请求,可通过addRequestHandler(requestHandler)添加。

    处理器的初始化在Picasso的构造函数中初始化,来看下默认都包含哪些处理器:

    // 默认有7个handler
    int builtInHandlers = 7;
    // 开发者添加的handler个数
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    List<RequestHandler> allRequestHanadlers = new ArrayList<>(builtInHandlers + extraCount);
    // 优先添加资源请求处理器
    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));
    // 添加asset请求处理器
    allRequestHandlers.add(new AssetRequestHandler(context));
    // 添加文件请求处理器
    allRequestHandlers.add(new FileRequestHandler(context));
    // 添加网络请求处理器
    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    requestHandlers = Collections.unmodifiableList(allRequestHandlers);
    

    从命名就可以看出这些处理器分别可以从资源、Asset、File、网络等地方加载图片。

    load

    初始化完成后获得一个Picasso对象,然后使用load(uri)方法设置图片uri

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

    这里仅仅做了一件事,就是创建RequestCreator对象并返回。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.picass = picasso;
        this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
    }
    

    RequestCreator持有Picasso对象引用,以及一个Request.Builder对象,那它是怎么创建图片请求的呢?来看下Request.Builder做了什么。

    Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
        this.uri = uri;
        this.resourceId = resourceId;
        this.config = bitmapConfig;
    }
    

    可以看到这里就是做了一些赋值操作,没有什么特别的,我们接着往下看。

    into

    设置好一堆配置之后,最后我们会调用into(target)方法,就发送了一次请求。into()方法是在RequestCreator中实现的,因为上面load()之后我们得到的是RequestCreator对象。Picasso支持将图片加载到ImageViewRemoteViewTarget等对象上面,这里我们以ImageView为例来解读代码。

    public void into(ImageView target) {
        into(target, null);
    }
    // 支持回调,这里的回调是强引用,可能导致Activity/Fragment内存不能被释放
    public void into(ImageView target, Callback callback) {
        // 请求开始时间
        long started = System.nanoTime();
        // 检查当前是否主线程,如果不是,抛出异常
        checkMain();
        // target为空,抛出异常
        if (target == null) {
            throw new IllegalArgumentException("Target must not be null.");
        }
        // 检查data中有没有图片的uri或resourceId
        if (!data.hasImage()) {
            // 没有uri或资源id,取消此次请求
            picasso.cancelRequest(target);
            // 显示预留图片
            if (setPlaceholder) {
                setPlaceholder(target, getPlaceholderDrawable());
            }
            return;
        }
        // 是否调用了fit(),表示要将图片调整为ImageView的大小
        if (deferred) {
            // fit不能与resize一起使用
            if (data.hasSize()) {
                throw new IllegalStateException("Fit cannot be used with resize.");
            }
            // 获取ImageView的大小
            int width = target.getWidth();
            int height = target.getHeight();
            if (width == 0 || height == 0) {
                if (setPlaceholder) {
                    setPlaceholder(target, getPlaceholderDrawable());
                }
                picasso.defer(target, new DeferredRequestCreator(this, target, callback));
                return;
            }
            data.resize(width, height);
        }
        // 构建请求
        Request request = createRequest(started);
        // 构建请求对应的key
        String requestKey = createKey(request);
        // 检查是否支持从缓存中读取
        if (shouldReadFromMemoryCache(memoryPolicy)) {
            Bitmap  bitmap = picasso.quickMemoryCacheCheck(requestKey);
            if (bitmap != null) {
                // 缓存命中,不再发送请求,并将图片显示到ImageView上
                picass.cancelRequest(target);
                setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
                if (callback != null) {
                    callback.onSuccess();
                }
                return;
            }
        }
        if (setPlaceholder) {
            setPlaceholder(target, getPlaceholderDrawable());
        }
        // 缓存未命中,创建请求任务Action,并将请求任务交给dispatcher处理
        Action action = 
                new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                        errorDrawable, requestKey, tag, callback, noFade);
        picasso.enqueueAndSubmit(action);
    }
    

    首先会先从缓存中获取图片,缓存命中则直接显示图片,不再发送请求;缓存未命中才发送请求。
    这里可能有个疑问,已经有了Request,为什么还有个Action,我们通过它们的参数看看它们之间的区别。

    public final class Request {
        int id;    // 请求的唯一识别id
        public final Uri uri;    // 图片的URI
        public final int resourceId;    // 图片的资源ID
        public final List<Transformation> transformations;
        public final int targetWidth;    // 用于resize的目标宽度
        public final int targetHeight;    // 用于resize的目标高度
        public final boolean centerCrop;    // 是否使用"centerCrop"缩放
        public final int centerCropGravity;
        public final boolean centerInside;
        public final float rotationDegrees;
        public final float rotationPivotX;
        public final float rotationPivotY;
        ...    // 各种图片变换参数
    }
    
    abstract class Action<T> {
        final Picasso picasso;
        final Request request;
        // 持有target的弱引用,不会因为长时间持有导致内存泄漏
        final WeakReference<T> target;
        final boolean noFade;  
        final int memoryPolicy;  
        final int networkPolicy;
        final int errorResId;
        final Drawable errorDrawable;
        final String key;
        final Object tag;
        boolean willReplay;
        boolean cancelld;
    }
    

    通过两个类的参数可以发现,Request关注的是请求本身,比如请求的uri、id、大小、变换参数等,而Action代表的则是任务本身,因此需要Picasso对象的引用,也需要Request对象的引用,同时还有一些网络策略、缓存策略,是否重载、是否取消等。
    需要注意的是,Action中持有的是Target对象的弱引用,不影响Target对象的回收。
    接下来我们看下enqueueAndSubmit()方法。

    final Map<Object, Action> targetToAction;
    ...
    this.targetToAction = new WeakHashMap<>();
    ...
    void enqueueAndSubmit(Action action) {
        Object target = action.getTarget();
        if (target != null && targetToAction.get(target) != action) {
            cancelExistingRequest(target);
            targetToAction.put(target, action);
        }
        submit(action);
    }
    

    targetToAction是一个TargetAction的映射表,表示一个目标上只允许有一个任务存在。如果当前Target对象有请求任务的话,就停止之前的任务,再重新添加关联并提交请求。

    void submit(Action action) {
        dispatcher.dispatchSubmit(action);
    }
    

    请求最终交由Dispatcher进行分发处理。

    Dispatcher

    在分析Dispatcher分发请求之前,我们先来了解下Dispatcher的工作环境。
    首先,我们回顾下当初是怎么创建Dispatcher对象的:

    Dispatcher dispatcher = new Dispatcher(context, service, HANDLER,   
                downloader, cache, stats);
    

    可以看到Dispatcher需要关联线程池service、主线程的HANDLER、下载器downloader、缓存cache、监控器stats等。
    先来看下线程池,默认使用PicassoExecutorService,继承自ThreadPoolExecutor,默认使用3个线程。但是Dispatcher注册了监听网络变化的广播接收器,用以监控网络变化,一旦网络发生变化,会触发PicassoEexcutorService调整线程数量:

    • Wifi情况:4个线程
    • 4G网络:3个线程
    • 3G网络:2个线程
    • 2G网络:1个线程
    • 其他:默认3个线程

    回到Dispatcher,来看下它的工作线程dispatcherThreadhandler

    class Dispatcher {
        ...
        final DispatcherThread dispatcherThread;
        ...
        final Handler handler;
        ...
        this.dispatcherThread = new DispatcherThread();
        this.dispatcherThread.start();
        this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
        ...
        private static class DispatcherHandler extends Handler {
            private final Dispatcher dispatcher;
            DispatcherHandler(Looper looper, Dispatcher dispatcher) {
                super(looper);
                this.dispatcher = dispatcher;
            }
            @Override
            public void handleMessage(final Message msg) {
                ...
            }
        }
    
        static class DispatcherThread extends HandlerThread {
            DispatcherThread() {
                super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
            }
        }
    }
    

    可以看到Dispatcher创建了一个DispatcherThread子线程,DispatcherThread继承自HandlerThread,然后在子线程的Looper对象引用传给了handler,即handler是在子线程处理消息的。那么我们可以合理推测Dispatcher的工作都是在子线程中进行的,是不是这样呢?我们接着看dispatchSubmit()

    void dispatchSummit(Action action) {
        handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
    }
    

    果不其然,Dispatcher在主线程发送消息到子线程的消息队列,然后在子线程处理这些消息。

    case REQUEST_SUBMIT: {
        Action action = (Action) msg.obj;
        dispatcher.performSubmit(action);
        break;
    }
    

    最终提交请求的工作交给了DispatcherperformSubmit()来进行,继续看。

    void performSubmit(Action action) {
        performSubmit(action, true);
    }
    
    void performSubmit(Action action, boolean dismissFailed) {
        // 如果请求已暂停,不分发
        if (pausedTags.contains(action.getTag())) {
            pausedActions.put(action.getTarget(), action);
            return;
        }
        BitmapHunter hunter = hunterMap.get(action.getKey());
        if (hunter != null) {
            // 相同的请求已经在线程池中,不再重复发送,而是关联到之前的请求中
            hunter.attach(action);
            return;
        }
        // 线程池已经关闭,不再做处理
        if (service.isShutdown()) {
            return;
        }
        // 创建hunter
        hunter = forRequest(action.getPicasso(), this, cache, stats, action);
        hunter.future = service.submit(hunter);
        hunterMap.put(action.getKey(), hunter);
        if (dismissFailed) {
            failedActions.remove(action.getTarget());
        }
    }
    

    一通判断之后,我们需要创建一个BitmapHunter对象,它继承自Runnable,可被线程池执行。先来看下forRequest()方法做了什么。

    static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache,
            Action action) {
        Request request = action.getRequest();
        List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
        // 从Picasso初始化的请求处理器列表中找到合适的处理器来处理请求
        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);
    }
    

    这里主要是查找合适的RequestHandler,对Picasso初始化时创建的处理器列表requestHandlers进行遍历,调用RequestHandlercanHandleRequest()方法,判断该处理器能否处理这个请求,从而找出合适的处理器。如果处理器列表中没有合适的RequestHandler,则会使用ERRORING_HANDLER,它在load()请求时会抛出异常。
    接下来看下BitmapHunterrun()方法。

    @Override
    public void run() {
        try {
            updateThreadName(data);
            result = hunt();
            if (result == null) {
                dispatcher.dispatchFailed(this);
            } else {
                dispatcher.dispatchComplete(this);
            }
        } catch (xxException e) {
            // 捕获了各种异常,ResponseException、IOException、
            // OutOMemoryError等
            ...
            dispatcher.dispatcherFailed(this)
        } finally {
            Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
        }
    }
    

    真正处理请求的操作放在了hunt()方法,我们接着往下看。

    Bitmap hunt() throws IOException {
        Bitmap bitmap = null;
        // 再次尝试从缓存中获取
        if (shouldReadFromMemoryCache(memoryPolicy)) {
            bitmap = cache.get(key);
            if (bitmap != null) {
                // cache hit
                stats.dispatchCacheHit();
                loadedFrom = MEMORY;
                return bitmap;
            }
        }
        // cache miss,通过RequestHandler加载图片
        networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
        // 调用RequestHandler的load()方法加载图片
        RequestHandler.Result result = requestHandler.load(data, networkPolicy);
        if (result != null) {
            loadedFrom = result.getLoadedFrom();
            exifOrientation = result.getExifOrientation();
            bitmap = result.getBitmap();
            // 如果没有Bitmap,需要对流进行解码
            if (bitmap == null) {
                Source source = result.getSource();
                try {
                    bitmap = decodeStream(source, data);
                } finally {
                    try {
                        source.close();
                    } catch (IOException ignored) {
                    }
                }
            }
        }
        if (bitmap != null) {
            stats.dispatchBitmapDecoded(bitmap);
            // 对图片进行变换操作
            if (data.needsTransformation() || exifOrientation != 0) {
                synchronized (DECODE_LOCK) {
                    if (data.needsMatrixTransform() || exifOrientataion != 0) {
                        // 对图片进行矩阵变换
                        bitmap = transformResult(data, bitmap, exifOrientation);
                    }
                    if (data.hasCustomTransformations()) {
                        // 对图片进行自定义变换
                        bitmap = applyCustomTransformations(data.transformations, bitmap);
                    }
                }
                if (bitmap != null) {
                    stats.dispatchBitmapTransformed(bitmap);
                }
            }
        }
        return bitmap;
    }
    

    我们以NetworkRequestHandler为例,来看下load()方法做了哪些事情。

    @Override
    public Result load(Request request, int networkPolicy) throws IOExcetpion {
        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);
        }
        // 判断加载来源,当且仅当响应完全来自网络时,cacheResponse为null
        Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;
        if (loadedFrom == DISK && body.contentLength() = 0) {
            body.close();
            throw new ContentLengthExceptioin("Received response with 0 content-length header.");
        }
        if (loadedFrom == NETWORK && body.contentLength() > 0) {
            stats.dispatchDownloadFinished(body.contentLength());
        }
        return new Result(body.source(), loadedFrom);
    }
    
    1. 调用downloader.load()方法获取响应结果,这里的downloader默认使用OkHttp3Downloader,也可在创建Picasso实例的时候自定义;
    2. 判断请求是否成功,不成功则抛出响应异常;
    3. 判断结果来源。因为默认使用OkHttp3Downloader,而OkHttp则使用DiskLruCache作为磁盘缓存。这里会判断响应是从磁盘缓存中读取的还是网络读取的;
    4. 如果结果来自磁盘缓存,且内容长度为0,抛出异常;
    5. 将请求结果封装成Result对象并返回。

    结果分发

    回顾下BitmapHunter#run()方法中,针对请求成功和失败的情况,分别调用了Dispatcher的不同方法:

    // 请求成功
    dispatcher.dispatchComplete(this);
    // 请求结果为null或抛出异常
    dispatcher.dispatchFailed(this);
    

    来看下Dispatcher怎么做的。

    void dispatchComplete(BitmapHunter hunter) {
        handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
    }
    
    void dispatchFailed(BitmapHunter hunter) {
        handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter);
    }
    
    case HUNTER_COMPLETE: {
        BitmapHunter hunter = (BitmapHunter) msg.obj;
        dispatcher.performComplete(hunter);
        break;
    }
    case HUNTER_DECODE_FAILED: {
        BitmapHunter hunter = (BitmapHunter) msg.obj;
        dispatcher.performError(hunter, false);
        break;
    }
    
    void performComplete(BitmapHunter hunter) {
        // 判断是否需要写入内存缓存
        if (shouldWriteToMemoryCache(hunter.getMemoryPolicy()) {
            // 写入内存缓存
            cache.set(hunter.getKey(), hunter.getResult());
        }
        // 将该任务从任务表中移除
        hunterMap.remove(hunter.getKey());
        // 对结果进行批处理
        batch(hunter);
    }
    
    void performError(BitmapHunter hunter, boolean willReplay) {
        // 将该任务从任务表中移除
        hunterMap.remove(hunter.getKey());
        // 对结果进行批处理
        batch(hunter);
    }
    

    可以看到,请求成功时,会先判断是否需要内存缓存,需要的话则将结果缓存。而不论是成功还是失败,最终都会调用batch()方法对结果批处理。

    private static final int BATCH_DELAY = 200;
    final List<BitmapHunter> batch;
    ...
    this.batch = new ArrayList<>(4);
    ...
    private void batch(BitmapHunter hunter) {
        // 如果请求取消,不作处理
        if (hunter.isCancelled()) {
            return;
        }
        if (hunter.result != null) {
            hunter.result.prepareToDraw();
        }
        batch.add(hunter);
        if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
            handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
        }
    }
    

    首先将结果存入batch列表中,如果消息队列中没有HUNTER_DELAY_NEXT_BATCH消息,则发送一个延迟消息,默认200ms。在这期间,所有的结果都会被添加到batch列表中,时间到了再一并处理,可防止短时间内大量消息造成消息队列拥堵。

    case HUNTER_DELAY_NEXT_BATCH: {
        dispatcher.performBatchComplete();
        break;
    }
    
    void performBatchComplete() {
        // 新建一份拷贝,然后将batch清空,以便继续接收新的结果
        List<BitmapHunter> copy = new ArrayList<>(batch);
        batch.clear();
        // 向主线程发送消息
        mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy);
        logBatch(copy);
    }
    

    我们知道DispatchermainThreadHandler是在创建时传入的,在Picasso类中实现。

    static final Handler HANDLER = new Handler(looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case HUNTER_BATCH_COMPLETE: {
                    List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
                    // 遍历处理每个结果
                    for (int i = 0, n = batch.size(); i < n; i++) {
                        BitmapHunter hunter = batch.get(i);
                        hunter.picasso.complete(hunter);
                    }
                    break;
                }
                // 其他消息
                ...
            }
        }
    }
    
    void complete(BitmapHunter hunter) {
        Action single = hunter.getAction();
        List<Action> joined = hunter.getActions();
        boolean hasMultiple = joined != null && !joined.isEmpty();
        boolean shouldDeliver = single != null || hasMultiple;
        if (!shouldDeliver) {
            return;
        }
        Uri uri = hunter.getData().uri;
        Exception exception = hunter.getException();
        Bitmap result = hunter.getResult();
        LoadedFrom from = hunter.getLoadedFrom();
        if (single != null) {
            deliverAction(result, from, single, exception);
        }
        if (hasMultiple) {
            for (int i = 0, n = joined.size(); i < n; i++) {
                Action join = joined.get(i);
                deliverAction(result, from, join, exception);
            }
        }
        if (listener != null && exception != null) {
            listener.onImageLoadFailed(this, uri, exception);
        }
    }
    

    这里首先会获取BitmapHunter的请求任务single以及关联的请求任务列表joined。还记得在将BitmapHunter添加线程池之前,会先判断该hunter是否已经在线程池中,如果是,则不会重复添加,而是将该hunter通过BitmapHunter#attach()方法关联到之前的任务上。这里的joined列表就是当前任务所关联的任务列表。
    不管是单个任务还是任务列表,都调用deliverAction()方法一一进行结果分发。

    private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
        if (action.isCancelled()) {
            return;
        }
        if (!action.willReplay()) {
            // 如果不需要重试,将target到action的关联移除
            targetToAction.remove(action.getTarget());
        }
        if (result != null) {
            if (from == null) {
                throw new AssertionError("LoadedFrom cannot be null");
            }
            action.complete(result, from);
        } else {
            action.error(e);
        }
    }
    

    最终调用的是Action类的complete()error()方法。Action是个抽象类,complete()error()是其定义的抽象方法,Picasso支持GetActionFetchActionTargetActionImageViewAction,以及RemoteViewAction。我们在创建Action时使用的是ImageViewAction,那么这里我们就以ImageViewAction为例。

    @Overrid
    public void complete(Bitmap result, Picasso.LoadedFrom from) {
        if (result == null) {
            throw new AssertionError(
                    String.format("Attempted to complete action with no result!\n%s", this));
        }
        // Action持有的是ImageView的弱引用,需要判空
        ImageView target = this.target.get();
        if (target == null) {
            return;
        }
        Context context = picasso.context;
        boolean indicatorsEnabled = picasso.indicatorsEnabled;
        PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
        if (callback != null) {
            callback.onSuccess();
        }
    }
    
    @Override
    public void error(Exception e) {
        ImageView target = this.target.get();
        if (target == null) {
            return;
        }
        // 如果预留图是动画,则停掉动画
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof Animatable) {
            ((Animatable) placeholder).stop);
        }
        // 设置错误图,优先判断资源ID
        if (errorResId != 0) {
            target.setImageResource(errorResId);
        } else if (errorDrawable != null) {
            target.setImageDrawable(errorDrawable);
        }
        if (callback != null) {
            callback.onError(e);
        }
    }
    
    • 请求成功,将返回的Bitmap及相关参数封装成PicassoDrawable(继承自BitmapDrawable),然后显示到ImageView上;
    • 请求失败,将之前设置的错误图片显示到ImageView上。

    需要注意的是,如果预留图placeholder是动画,在设置新的图片资源前需要先把动画停掉。

    到此,Picasso的整个流程分析完毕,下面是整个流程的时序图(根据最新版本的代码,修改了所参考文章的时序图,非原创)

    picasso.jpg
    参考:
    https://blog.csdn.net/chdjj/article/details/49964901

    相关文章

      网友评论

          本文标题:Picasso 源码解析

          本文链接:https://www.haomeiwen.com/subject/qzkghqtx.html