美文网首页
Picasso流程解析

Picasso流程解析

作者: 陆元伟 | 来源:发表于2019-03-19 15:23 被阅读0次

    Picasso流程解析

    首先从调用代码看起

    // Trigger the download of the URL asynchronously into the image view.
    
    Picasso.with(context)
    .load(url)
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.error)
    .resizeDimen(R.dimen.list_detail_image_size,R.dimen.list_detail_image_size)
    .centerInside()
    .into(holder.image);
    

    很显然是单例模式

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

    再看load方法,对参数做了检查,调用了一个重载方法,返回的是一个RequestCreator对象

    public RequestCreator load(@Nullable 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));
    }
    

    String转换为Uri,直接生成一个RequestCreator对象,并把uri放入里面然后返回。后面的方法就在该对象里面调用了

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

    而这个uri。则放入到data里面,这个data是,Request的一个内部类Builder.

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

    再往下看。placeholder方法就是设置还未下载图片之前占位资源id,这样先显示的就是这张图片

    public RequestCreator placeholder(@DrawableRes int placeholderResId) {
            //默认这个值为true,如果调用noPlaceholder方法则为false
           if(!setPlaceholder) {
                   throw new IllegalStateException("Already explicitly declared as no placeholder.");
    
             }
    
            if(placeholderResId ==0) {
                  throw new IllegalArgumentException("Placeholder image resource invalid.");
              }
                //如果设置过placeholderDrawable,再调用这个方法就会抛异常。
              if(placeholderDrawable!=null) {
                 throw new IllegalStateException("Placeholder image already set.");
              }
    
             this.placeholderResId= placeholderResId;
    
             return this;
    
        }
    

    error方法设置如果发生错误是显示资源Id,和placeholder方法一样。

    /** An error drawable to be used if the request image could not be loaded. */
    
    public RequestCreator error(@DrawableRes int  errorResId) {
    
        if(errorResId ==0) {
            throw new  IllegalArgumentException("Error image resource invalid.");
        }
        if(errorDrawable!=null) {
            throw new  IllegalStateException("Error image already set.");
        }
        this.errorResId= errorResId;
        return this;
    
    }
    

    接下来是resizeDimen方法,获取宽高。

     public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) {
            Resources resources = this.picasso.context.getResources();
            int targetWidth = resources.getDimensionPixelSize(targetWidthResId);
            int targetHeight = resources.getDimensionPixelSize(targetHeightResId);
            return this.resize(targetWidth, targetHeight);
        }
    

    然后把宽高放到data里面,这个data是Request的内部类Builder.

    public RequestCreator resize(int targetWidth, int targetHeight) {
            this.data.resize(targetWidth, targetHeight);
            return this;
        }
    

    centerInside方法,也是调用data的方法。

     public RequestCreator centerInside() {
            this.data.centerInside();
            return this;
        }
    

    最后看into方法。调用重载方法

     public void into(ImageView target) {
            this.into(target, (Callback)null);
      }
    

    这个方法代码比较长。

    首先检查是否在主线程调用。如果不是则直接抛异常。因为后面要设置控件占位图片,或者从缓存中读取图片设置给控件,所有该方法一定要在主线程调用。

    然后一系列参数判断。不过默认的值都会走到生成reqeust
    利用request的信息生成key,因为缓存就是根据该key去获取的。
    默认内存缓存是开启的。从内存缓存中获取,获取到了直接设置返回,啥事也就没有了。
    没有缓存则判断是否设置了占位图片,如果设置了则先显示占位图片,根据生成一个Action.调用picasso的方法添加

    public void into(ImageView target, Callback callback) {
            long started = System.nanoTime();
            //检查是否在主线程。不是则抛出异常。
            Utils.checkMain();
            if (target == null) {
                throw new IllegalArgumentException("Target must not be null.");
            //这里是查看data里面是否有uri.或者resourceId.因为前面构造RequestCreator的时候把uri传给了data.所以hasImage返回true
            } else if (!this.data.hasImage()) {
                this.picasso.cancelRequest(target);
                if (this.setPlaceholder) {
                    PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
                }
    
            } else {
                //默认这个值是false.
                if (this.deferred) {
                    if (this.data.hasSize()) {
                        throw new IllegalStateException("Fit cannot be used with resize.");
                    }
                    int width = target.getWidth();
                    int height = target.getHeight();
                    if (width == 0 || height == 0) {
                        if (this.setPlaceholder) {
                            PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
                        }
                        this.picasso.defer(target, new DeferredRequestCreator(this, target, callback));
                        return;
                    }
    
                    this.data.resize(width, height);
                }
                //利用data创造一个Request.因为之前设置的属性都设置到data里面。所以这里创建的时候传到了Request里面
                Request request = this.createRequest(started);
                //利用request的一些信息生成key.
                String requestKey = Utils.createKey(request);
                //如果设置了NO_CACHE,则不从内存缓存中获取。否则先从内存缓存中获取。默认缓存是LruCache
                if (MemoryPolicy.shouldReadFromMemoryCache(this.memoryPolicy)) {
                    Bitmap bitmap = this.picasso.quickMemoryCacheCheck(requestKey);
                    //如果缓存中有。则取消请求。设置到目标控件上
                    if (bitmap != null) {
                        this.picasso.cancelRequest(target);
                        PicassoDrawable.setBitmap(target, this.picasso.context, bitmap, LoadedFrom.MEMORY, this.noFade, this.picasso.indicatorsEnabled);
                        if (this.picasso.loggingEnabled) {
                            Utils.log("Main", "completed", request.plainId(), "from " + LoadedFrom.MEMORY);
                        }
    
                        if (callback != null) {
                            callback.onSuccess();
                        }
    
                        return;
                    }
                }
                //如果设置了占位图片,先设置占位图片
                if (this.setPlaceholder) {
                    PicassoDrawable.setPlaceholder(target, this.getPlaceholderDrawable());
                }
                //生成一个Action
                Action action = new ImageViewAction(this.picasso, target, request, this.memoryPolicy, this.networkPolicy, this.errorResId, this.errorDrawable, requestKey, this.tag, callback, this.noFade);
                //添加到队列并且提交
                this.picasso.enqueueAndSubmit(action);
            }
        }
    

    小结:with方法是创建一个Picasso单例出来,load方法是创建一个RequestCreator对象出来。后面的所有链式调用就都是在RequestCreator对象里面。RequestCreator对象主要是接收参数,并且校验参数。并且从内存缓存里面,如果存在则不用去网络请求,也就没RequestAction的什么事了。如果不存在,把一些必要的参数放到Action对象,然后提交出去。

    下面再看看怎么处理网络请求的,接着上面从PicassoenqueueAndSubmit看起

    void enqueueAndSubmit(Action action) {
            Object target = action.getTarget();
            //如果已经有了该action.则取消。再重新放入
            if (target != null && this.targetToAction.get(target) != action) {
                this.cancelExistingRequest(target);
                this.targetToAction.put(target, action);
            }
    
            this.submit(action);
        }
    
    void submit(Action action) {
            this.dispatcher.dispatchSubmit(action);
     }
    

    调用Dispatcher对象的dispatchSubmit方法,发送一个what为1的message,该handler是一个内部类,并且不是获取主线程的loop。而是获取内部类DispatcherThreadloop。该类继承HandlerThread。也就是说handler是在子线程中处理Message的。

     void dispatchSubmit(Action action) {
            this.handler.sendMessage(this.handler.obtainMessage(1, action));
        }
    

    handlerMessage方法里面处理消息为1的地方,调用了performSubmit方法。此时已经切换到子线程中运行了

    public void handleMessage(final Message msg) {
            Object tag;
            BitmapHunter hunter;
            Action action;
            switch(msg.what) {
            case 1:
                action = (Action)msg.obj;
                this.dispatcher.performSubmit(action);
                break;
            case 2:
    

    调用重载方法

     void performSubmit(Action action) {
            this.performSubmit(action, true);
        }
    

    注释1.判断当前action的是否暂停了。如果暂停了。就不去请求。这个在配合列表使用更好。因为当滑动列表时,会产生很多请求,这个时候如果滑动的时候调用pauseTag方法,则就不会请求服务器。滑动完成后调用resumeTag,又会去请求服务器

    注释2 判断是否有相同的action。这样就不用重复添加。
    注释3则利用action,和其他信息生成一个BitmapHunter对象。该对象继承了Runnable对象。并且把该对象放入线程池中。既然是Runnable对象。那就看run方法

      void performSubmit(Action action, boolean dismissFailed) {
            //1判断集合Set中是否存在,由于没设置过,因此走else.如果设置了。则就不会请求。如果在列表中滑动会生成很多action。这样不可见的ImageView就可以设置pausedTag。
            if (this.pausedTags.contains(action.getTag())) {
                this.pausedActions.put(action.getTarget(), action);
                if (action.getPicasso().loggingEnabled) {
                    Utils.log("Dispatcher", "paused", action.request.logId(), "because tag '" + action.getTag() + "' is paused");
                }
    
            } else {
                //2 由于没设置过,故为null
                BitmapHunter hunter = (BitmapHunter)this.hunterMap.get(action.getKey());
                if (hunter != null) {
                    hunter.attach(action);
                //线程池是否关闭了
                } else if (this.service.isShutdown()) {
                    if (action.getPicasso().loggingEnabled) {
                        Utils.log("Dispatcher", "ignored", action.request.logId(), "because shut down");
                    }
    
                } else {
                    //3生成了一个BitmapHunter对象,该对象继承了Runnable
                    hunter = BitmapHunter.forRequest(action.getPicasso(), this, this.cache, this.stats, action);
                    //提交到线程池
                    hunter.future = this.service.submit(hunter);
                    
                    //把action的key 和hunter绑定在集合
                    this.hunterMap.put(action.getKey(), hunter);
                    if (dismissFailed) {
                        this.failedActions.remove(action.getTarget());
                    }
    
                    if (action.getPicasso().loggingEnabled) {
                        Utils.log("Dispatcher", "enqueued", action.request.logId());
                    }
    
                }
            }
        }
    

    BitmapHunter对象的run方法,设置线程名。然后调用hunt方法去获取。然后返回相应的回调。如果出现了异常,根据不同异常执行重试或者回调失败

     public void run() {
            try {
                //设置线程名
                updateThreadName(this.data);
                if (this.picasso.loggingEnabled) {
                    Utils.log("Hunter", "executing", Utils.getLogIdsForHunter(this));
                }
    
                this.result = this.hunt();
                if (this.result == null) {
                    this.dispatcher.dispatchFailed(this);
                } else {
                    this.dispatcher.dispatchComplete(this);
                }
            } catch (ResponseException var10) {
                if (!var10.localCacheOnly || var10.responseCode != 504) {
                    this.exception = var10;
                }
    
                this.dispatcher.dispatchFailed(this);
            } catch (ContentLengthException var11) {
                this.exception = var11;
                //重新提交请求。
                this.dispatcher.dispatchRetry(this);
            } catch (IOException var12) {
                this.exception = var12;
                //重新提交请求。
                this.dispatcher.dispatchRetry(this);
            } catch (OutOfMemoryError var13) {
                StringWriter writer = new StringWriter();
                this.stats.createSnapshot().dump(new PrintWriter(writer));
                this.exception = new RuntimeException(writer.toString(), var13);
                this.dispatcher.dispatchFailed(this);
            } catch (Exception var14) {
                this.exception = var14;
                this.dispatcher.dispatchFailed(this);
            } finally {
                Thread.currentThread().setName("Picasso-Idle");
            }
    
        }
    

    hunt方法。这个方法比较长。看主要部分的,这里也先是取缓存中获取。如果存在,则直接返回。如果不存在,则调用requestHandler去处理。
    Bitmap hunt() throws IOException {
    Bitmap bitmap = null;
    //1 如果可以从缓存中获取。去缓存获取。
    if (MemoryPolicy.shouldReadFromMemoryCache(this.memoryPolicy)) {
    bitmap = this.cache.get(this.key);
    if (bitmap != null) {
    this.stats.dispatchCacheHit();
    this.loadedFrom = LoadedFrom.MEMORY;
    if (this.picasso.loggingEnabled) {
    Utils.log("Hunter", "decoded", this.data.logId(), "from cache");
    }

                    return bitmap;
                }
            }
    
            this.data.networkPolicy = this.retryCount == 0 ? NetworkPolicy.OFFLINE.index : this.networkPolicy;
            //2 通过requestHandler去获取
            Result result = this.requestHandler.load(this.data, this.networkPolicy);
           ...
            }
    
            return bitmap;
        }
    

    requestHandler是创建BitmapHunter的时候传进来的。这里获取Picasso里面RequestHandler的集合。而requestHandlers里面有什么呢?

      static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
            Request request = action.getRequest();
            List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
            int i = 0;
    
            for(int count = requestHandlers.size(); i < count; ++i) {
                RequestHandler 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);
        }
    

    创建Picasso的时候就已经放置很多RequestHandler在里面,根据load方法传进来的字符串资源分别对应不一样的处理对象。这样我们就可以在load方法里面传入资源文件或者文件路径或者网络url等等

            List<RequestHandler> allRequestHandlers = 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));
            allRequestHandlers.add(new AssetRequestHandler(context));
            allRequestHandlers.add(new FileRequestHandler(context));
            allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
            this.requestHandlers = Collections.unmodifiableList(allRequestHandlers);
    

    由于我们传进来的是url。所以这里的requestHandler就是NetworkRequestHandler处理

    NetworkRequestHandler方法处理,这个downloader也是Picasso类里面创建的。并且里面用的是OkHttp

     public Result load(Request request, int networkPolicy) throws IOException {
            Response response = this.downloader.load(request.uri, request.networkPolicy);
            if (response == null) {
                return null;
            } else {
                LoadedFrom loadedFrom = response.cached ? LoadedFrom.DISK : LoadedFrom.NETWORK;
                Bitmap bitmap = response.getBitmap();
                //返回bitmap
                if (bitmap != null) {
                    return new Result(bitmap, loadedFrom);
                } else {
                    InputStream is = response.getInputStream();
                    if (is == null) {
                        return null;
                    } else if (loadedFrom == LoadedFrom.DISK && response.getContentLength() == 0L) {
                        Utils.closeQuietly(is);
                        throw new NetworkRequestHandler.ContentLengthException("Received response with 0 content-length header.");
                    } else {
                        if (loadedFrom == LoadedFrom.NETWORK && response.getContentLength() > 0L) {
                            this.stats.dispatchDownloadFinished(response.getContentLength());
                        }
    
                        return new Result(is, loadedFrom);
                    }
                }
            }
        }
    

    继续看hunt方法

     Bitmap hunt() throws IOException {
            Bitmap bitmap = null;
           ...
            Result result = this.requestHandler.load(this.data, this.networkPolicy);
            if (result != null) {
                this.loadedFrom = result.getLoadedFrom();
                this.exifRotation = result.getExifOrientation();
                bitmap = result.getBitmap();
                if (bitmap == null) {
                    InputStream is = result.getStream();
    
                    try {
                        bitmap = decodeStream(is, this.data);
                    } finally {
                        Utils.closeQuietly(is);
                    }
                }
            }
    
            if (bitmap != null) {
                if (this.picasso.loggingEnabled) {
                    Utils.log("Hunter", "decoded", this.data.logId());
                }
    
                this.stats.dispatchBitmapDecoded(bitmap);
                //如果需要缩放或者旋转处理。
                if (this.data.needsTransformation() || this.exifRotation != 0) {
                    Object var10 = DECODE_LOCK;
                    synchronized(DECODE_LOCK) {
                        if (this.data.needsMatrixTransform() || this.exifRotation != 0) {
                            bitmap = transformResult(this.data, bitmap, this.exifRotation);
                            if (this.picasso.loggingEnabled) {
                                Utils.log("Hunter", "transformed", this.data.logId());
                            }
                        }
    
                        if (this.data.hasCustomTransformations()) {
                            bitmap = applyCustomTransformations(this.data.transformations, bitmap);
                            if (this.picasso.loggingEnabled) {
                                Utils.log("Hunter", "transformed", this.data.logId(), "from custom transformations");
                            }
                        }
                    }
    
                    if (bitmap != null) {
                        this.stats.dispatchBitmapTransformed(bitmap);
                    }
                }
            }
    
            return bitmap;
        }
    

    继续往前看hunt调用的地方,如果已经获取到bitmap了,调用dispatcherdispatchComplete方法

         public void run() {
            try {
                //设置线程名
                updateThreadName(this.data);
                if (this.picasso.loggingEnabled) {
                    Utils.log("Hunter", "executing", Utils.getLogIdsForHunter(this));
                }
    
                this.result = this.hunt();
                if (this.result == null) {
                    this.dispatcher.dispatchFailed(this);
                } else {
                    this.dispatcher.dispatchComplete(this);
                }
            } 
            ...
    
        }
    

    DispatcherdispatchComplete方法

    void dispatchComplete(BitmapHunter hunter) {
            this.handler.sendMessage(this.handler.obtainMessage(4, hunter));
        }
    
      case 4:````````
       hunter = (BitmapHunter)msg.obj;
       this.dispatcher.performComplete(hunter);
        break;
    

    调用performComplete方法,设置了缓存

     void performComplete(BitmapHunter hunter) {
            if (MemoryPolicy.shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
                this.cache.set(hunter.getKey(), hunter.getResult());
            }
    
            this.hunterMap.remove(hunter.getKey());
            this.batch(hunter);
            if (hunter.getPicasso().loggingEnabled) {
                Utils.log("Dispatcher", "batched", Utils.getLogIdsForHunter(hunter), "for completion");
            }
    
        }
    

    又发了一个消息

      private void batch(BitmapHunter hunter) {
            if (!hunter.isCancelled()) {
                this.batch.add(hunter);
                if (!this.handler.hasMessages(7)) {
                    this.handler.sendEmptyMessageDelayed(7, 200L);
                }
    
            }
        }
    

    处理消息。调用了performBatchComplete

     public void handleMessage(final Message msg) {
            Object tag;
            BitmapHunter hunter;
            Action action;
            switch(msg.what) {
           ...
            case 7:
                this.dispatcher.performBatchComplete();
                break;
    

    发到主线程中去了,这主线程是Pacasso里面的

      void performBatchComplete() {
            List<BitmapHunter> copy = new ArrayList(this.batch);
            this.batch.clear();
            this.mainThreadHandler.sendMessage(this.mainThreadHandler.obtainMessage(8, copy));
            this.logBatch(copy);
        }
    

    Picasso里面的handleMessage方法

      public void handleMessage(Message msg) {
               ...
                case 8:
                    batch = (List)msg.obj;
                    i = 0;
    
                    for(n = batch.size(); i < n; ++i) {
                        BitmapHunter hunter = (BitmapHunter)batch.get(i);
                        hunter.picasso.complete(hunter);
                    }
    
                    return;
    

    调用了complete方法,我们这里的single是个ImageViewAction.后deliverAction会调用ImageViewAction里面的complete方法

     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) {
                Uri uri = hunter.getData().uri;
                Exception exception = hunter.getException();
                Bitmap result = hunter.getResult();
                Picasso.LoadedFrom from = hunter.getLoadedFrom();
                if (single != null) {
                    this.deliverAction(result, from, single);
                }
    
                if (hasMultiple) {
                    int i = 0;
    
                    for(int n = joined.size(); i < n; ++i) {
                        Action join = (Action)joined.get(i);
                        this.deliverAction(result, from, join);
                    }
                }
    
                if (this.listener != null && exception != null) {
                    this.listener.onImageLoadFailed(this, uri, exception);
                }
    
            }
        }
    

    ImageViewActioncomplete方法。

       public void complete(Bitmap result, LoadedFrom from) {
            if (result == null) {
                throw new AssertionError(String.format("Attempted to complete action with no result!\n%s", this));
            } else {
                ImageView target = (ImageView)this.target.get();
                if (target != null) {
                    Context context = this.picasso.context;
                    boolean indicatorsEnabled = this.picasso.indicatorsEnabled;
                    PicassoDrawable.setBitmap(target, context, result, from, this.noFade, indicatorsEnabled);
                    if (this.callback != null) {
                        this.callback.onSuccess();
                    }
    
                }
            }
        }
    
    
    }
    

    至此完成
    小结:Dispatcher里面有个DispatcherThread线程,该线程继承HandlerThread类,里面的DispatcherHandler类用的就是DispatcherThreadloop.这样就可以方便的把代码切换到子线程。主要的工作是在BitmapHunter类的run方法。并且里面的下载用的是Okhttp。下载完成后如果设置了大小的,或者自定义处理的。都在获取后操作bitmap的。

    相关文章

      网友评论

          本文标题:Picasso流程解析

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