美文网首页
Android图片加载框架 Picasso 源码解析(with-

Android图片加载框架 Picasso 源码解析(with-

作者: DaphyNeo | 来源:发表于2018-04-01 02:09 被阅读0次

    基本用法

    从最基本的用法开始

    Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
    

    1,with

    让我们先从with方法开始

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

    一个标准懒汉式Double Check单例模式,

        /** 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();
        }
    
        /** Create the {@link Picasso} instance. */
        public Picasso build() {
          Context context = this.context;
    
    //下载器
          if (downloader == null) {
            downloader = Utils.createDefaultDownloader(context);
          }
    //LRU缓存
          if (cache == null) {
            cache = new LruCache(context);
          }
    //线程池
          if (service == null) {
            service = new PicassoExecutorService();
          }
          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);
        }
      }
    

    内部使用构建者模式,将downloader(下载图片用),lru缓存(内存缓存),线程池(执行下载解析图片的Runnable),dispatcher(picasso内部事件调度者)初始化
    最后我们可以获取一个单例的Picasso实例
    让我们接下来看load方法


    image.png

    2,load方法

    在picasso类中有4个重载,我们先看String类型的

      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));
      }
    

    如果path为空,直接回返回一个RequestCreator,如果不为空,会调用Uri的重载
    让我们看看File对象的重载

      public RequestCreator load(File file) {
        if (file == null) {
          return new RequestCreator(this, null, 0);
        }
        return load(Uri.fromFile(file));
      }
    

    同样,最后也是调用Uri的重载,

      /**
       * Start an image request using the specified URI.
       * <p>
       * Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder,
       * if one is specified.
       *
       * @see #load(File)
       * @see #load(String)
       * @see #load(int)
       */
      public RequestCreator load(Uri uri) {
        return new RequestCreator(this, uri, 0);
      }
    

    int类型的重载和上面不一样,入参是资源ID

      public RequestCreator load(int resourceId) {
        if (resourceId == 0) {
          throw new IllegalArgumentException("Resource ID must not be zero.");
        }
        return new RequestCreator(this, null, resourceId);
      }
    

    好了,以上几个方法,每次调用都会生成一个RequestCreator对象,让我们看看他的构造方法

      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);
      }
    

    里面用构建者模式,开始准备一个request,但是还没有发送

    Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
    

    从load方法后,我们实际调用的是RequestCreator对象,我们可以根据需求,调用RequestCreator方法
    往Request里修改东西,比如centerCrop等等


    image.png

    3,into

    接下来看into方法,into方法有5个重载


    image.png

    我们先从ImageView的重载开始看

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

    调用两个方法的重载

      /**
       * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
       * target {@link Callback} if it's not {@code null}.
       * <p>
       * <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
       * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
       * you use this method, it is <b>strongly</b> recommended you invoke an adjacent
       * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
       */
      public void into(ImageView target, Callback callback) {
        long started = System.nanoTime();
        checkMain();
    
        if (target == null) {
          throw new IllegalArgumentException("Target must not be null.");
        }
    
        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();
          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);
        String requestKey = createKey(request);
    
        if (shouldReadFromMemoryCache(memoryPolicy)) {
          Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
          if (bitmap != null) {
            picasso.cancelRequest(target);
            setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
            if (picasso.loggingEnabled) {
              log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
            }
            if (callback != null) {
              callback.onSuccess();
            }
            return;
          }
        }
    
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
    
        Action action =
            new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                errorDrawable, requestKey, tag, callback, noFade);
    
        picasso.enqueueAndSubmit(action);
      }
    

    方法比较长,我们一点点来看

      checkMain();
    
      static void checkMain() {
        if (!isMain()) {
          throw new IllegalStateException("Method call should happen from the main thread.");
        }
      }
    
    
      static boolean isMain() {
        return Looper.getMainLooper().getThread() == Thread.currentThread();
      }
    

    检测线程,如果调用into方法的线程不是主线程会报异常
    "Method call should happen from the main thread."

    再往下看

    if (!data.hasImage()) {
          picasso.cancelRequest(target);
          if (setPlaceholder) {
            setPlaceholder(target, getPlaceholderDrawable());
          }
          return;
    }
    
        boolean hasImage() {
          return uri != null || resourceId != 0;
        }
    

    调用request对象hasImage,如果uri和resourceId都没有,就会取消交易,如果有图片占位符,则会直接显示占位符
    接下来看

    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();
          //如果宽和高中有一个是0
          if (width == 0 || height == 0) {
          //如果设置过图片占位符,则会先显示占位符
            if (setPlaceholder) {
              setPlaceholder(target, getPlaceholderDrawable());
            }
            //调用defer方法,延迟调用任务,返
            picasso.defer(target, new DeferredRequestCreator(this, target, callback));
            return;
          }
          //如果target这个ImageView宽高都不为0,则,图片之后会被调整大小和ImageView一样大
          data.resize(width, height);
        }
    

    deferred是一个RequestCreator类的成员变量,是否延期的意思,如果之前调用过RequestCreator的fit方法,会把deferred值为true

      /**
       * Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This
       * will result in delayed execution of the request until the {@link ImageView} has been laid out.
       * <p>
       * <em>Note:</em> This method works only when your target is an {@link ImageView}.
       */
      public RequestCreator fit() {
        deferred = true;
        return this;
      }
    

    让我们继续into方法继续往下看

       Request request = createRequest(started);
    
     /** Create the request optionally passing it through the request transformer. */
      private Request createRequest(long started) {
        int id = nextId.getAndIncrement();
    
    //这里通过build把request对象,构建好了
        Request request = data.build();
        request.id = id;
        request.started = started;
    
        boolean loggingEnabled = picasso.loggingEnabled;
        if (loggingEnabled) {
          log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
        }
    
    //如果之前有设置过requestTransformer,会对request进行transform
        Request transformed = picasso.transformRequest(request);
        if (transformed != request) {
          // If the request was changed, copy over the id and timestamp from the original.
          transformed.id = id;
          transformed.started = started;
    
          if (loggingEnabled) {
            log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
          }
        }
    
        return transformed;
      }
    

    我们继续从into往下看

    String requestKey = createKey(request);
    

    这个方法,主要是之后图片内存缓存标志
    接下来看

        //判断是否从内存获取图片
        if (shouldReadFromMemoryCache(memoryPolicy)) {
          //根据之前生成的key,从内存中获取缓存的bitmap
          Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
          //如果内存中有,则会完成请求
          if (bitmap != null) {
            picasso.cancelRequest(target);
            //将图片设置给ImageView
            setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
            if (picasso.loggingEnabled) {
              log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
            }
            //如果设置了回调,则会调用onSuccess
            if (callback != null) {
              callback.onSuccess();
            }
            return;
          }
        }
    

    我们继续into方法往下看

        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
    

    设置占位符

        Action action =
            new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                errorDrawable, requestKey, tag, callback, noFade);
    
        picasso.enqueueAndSubmit(action);
    

    到这里,我们要开始异步请求图片了

      void enqueueAndSubmit(Action action) {
        Object target = action.getTarget();
        if (target != null && targetToAction.get(target) != action) {
          //如果现在map中有一个ImageView正在进行的请求,或者还没有请求
          // This will also check we are on the main thread.
          // 取消当前的请求
          cancelExistingRequest(target);
          targetToAction.put(target, action);//将target为key,action为值,塞进map中
        }
        submit(action);
      }
    

    让我们来看看submit方法

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

    调用Dispatcher来之前创建的Action


    image.png

    dispatcher在picasso.builder build方法中创建

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

    内部通过handler来传递消息


    image.png

    这个handler再Dispatcher构造方法中初始化,传入的DispatcherThread的looper,DispatcherThread继承自HandlerThread,当这个线程运行的时候,会自动创建一个looper
    实际上走的是DispatcherHandler,让我们看看这个类

    image.png

    找到handlerMessage中REQUEST_SUBMIT这个分支,调用了dispatcher的performSubmit的方法

    void performSubmit(Action action) {
        performSubmit(action, true);
      }
    
      void performSubmit(Action action, boolean dismissFailed) {
    //如果当前action处于pause状态,会暂停加载,并且放入pausedActions中缓存起来
        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());
        }
      }
    

    先看看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
    
    //遍历requestHandlers,看看是否有可以处理这个request的handler
        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);
          }
        }
    //没找到会把一个错误的默认handler传回去
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
      }
    

    最后会返回一个BitmapHunter,他继承自Runnable
    我们继续看之前Dispatcher类的performSubmit方法

    hunter.future = service.submit(hunter);
    

    会把刚刚生产的hunter交给线程池去执行,这个线程池也是在picasso构建的时候生成

    image.png

    我们继续看刚刚的BitmapHunter,既然是Runnable,我们先看看他的run方法

    @Override public 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);
          }
    
           ·····
    
      }
    

    调用hunt方法,result就是返回的bitmap,那么核心从硬盘网络读取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;
          }
        }
    
        data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    //通过之前匹配的RequestHandler来处理这个request
        RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    
        ···
    
        return bitmap;
      }
    

    我们回头看,在picasso构造的时候,已经加入了好多个RequestHandler


    image.png

    从这里我们可以看到picasso支持的几种获取图片的途径
    依次是联系人相册,媒体库,ContentResolver,asset,文件,网络

    我们随便挑一个看看


    image.png

    如果之前Picasso的load方法传入的是file对象,则会走FileRequestHandler
    如果之前传入的string里是http路径,则会走NetworkRequestHandler

    让我们来看看NetworkRequestHandler的实现

      @Override public Result load(Request request, int networkPolicy) throws IOException {
    //调用downloader的load方法, downloader有两个实现,一个是okhttp,一个是urlconnection
        Response response = downloader.load(request.uri, request.networkPolicy);
    //如果没有结果,则会返回
        if (response == null) {
          return null;
        }
    
        Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
    
    //获取图片
        Bitmap bitmap = response.getBitmap();
        if (bitmap != null) {
    //如果获取到了图片,则会返回
          return new Result(bitmap, loadedFrom);
        }
    
        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.
        if (loadedFrom == DISK && response.getContentLength() == 0) {
          Utils.closeQuietly(is);
          throw new ContentLengthException("Received response with 0 content-length header.");
        }
        if (loadedFrom == NETWORK && response.getContentLength() > 0) {
          stats.dispatchDownloadFinished(response.getContentLength());
        }
        return new Result(is, loadedFrom);
      }
    

    最新的方法里,downloader返回的response都不会直接返回bitmap,而是inputstream
    最后load方法会把inputsteam流封装人result返回

    回到BitmapHunter hunt方法

      Bitmap hunt() throws IOException {
    
        ```
        RequestHandler.Result result = requestHandler.load(data, networkPolicy);
        if (result != null) {
          loadedFrom = result.getLoadedFrom();
          exifRotation = result.getExifOrientation();
    
          bitmap = result.getBitmap();
    
          // If there was no Bitmap then we need to decode it from the stream.
          if (bitmap == null) {
            InputStream is = result.getStream();
            try {
    //从inputstream转换成bitmap
              bitmap = decodeStream(is, data);
            } finally {
              Utils.closeQuietly(is);
            }
          }
        }
    
        if (bitmap != null) {
          if (picasso.loggingEnabled) {
            log(OWNER_HUNTER, VERB_DECODED, data.logId());
          }
          stats.dispatchBitmapDecoded(bitmap);
          if (data.needsTransformation() || exifRotation != 0) {
            synchronized (DECODE_LOCK) {
              if (data.needsMatrixTransform() || exifRotation != 0) {
    //如果图片中Exif有旋转信息,进行旋转
                bitmap = transformResult(data, bitmap, exifRotation);
                if (picasso.loggingEnabled) {
                  log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
                }
              }
              if (data.hasCustomTransformations()) {
    //如果requestCreator设置了transformation
                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;
      }
    

    我们先来看decodeStream方法

      /**
       * Decode a byte stream into a Bitmap. This method will take into account additional information
       * about the supplied request in order to do the decoding efficiently (such as through leveraging
       * {@code inSampleSize}).
       */
      static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
        MarkableInputStream markStream = new MarkableInputStream(stream);
        stream = markStream;
    
        long mark = markStream.savePosition(65536); // TODO fix this crap.
    
        final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
        final boolean calculateSize = RequestHandler.requiresInSampleSize(options);
    
        boolean isWebPFile = Utils.isWebPFile(stream);
        markStream.reset(mark);
        // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
        // Decode byte array instead
        if (isWebPFile) {
          byte[] bytes = Utils.toByteArray(stream);
          if (calculateSize) {
            BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
            RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
                request);
          }
          return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        } else {
          if (calculateSize) {
            BitmapFactory.decodeStream(stream, null, options);
            RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
                request);
    
            markStream.reset(mark);
          }
          Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
          if (bitmap == null) {
            // Treat null as an IO exception, we will eventually retry.
            throw new IOException("Failed to decode stream.");
          }
          return bitmap;
        }
      }
    

    在这里获取request中的图片格式(config) 和 设置好的宽高(resize方法)
    config如果之前没有设置,默认是ARGB_8888,
    如果设置了resize,在这里则会使用inSampleSize调整图片大小,否则会按默认大小加载
    然后回到run方法

    image.png

    返回给dispatcher

      void dispatchComplete(BitmapHunter hunter) {
        handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
      }
    

    回到DispatcherHandler

    image.png

    去到dispatcher的performComplete

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

    在这里,根据对request设置的内存策略,判断需不需要内存缓存,
    如果需要,会放入LRUCache里
    重点看batch,准备批量任务

      private void batch(BitmapHunter hunter) {
        if (hunter.isCancelled()) {
          return;
        }
        batch.add(hunter);
        if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
          handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
        }
      }
    

    回到DispatcerHandler

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

    批量任务都已经准备就绪

      static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
        @Override public void handleMessage(Message msg) {
          switch (msg.what) {
            case HUNTER_BATCH_COMPLETE: {
              @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
              //noinspection ForLoopReplaceableByForEach
              for (int i = 0, n = batch.size(); i < n; i++) {
                BitmapHunter hunter = batch.get(i);
    //
                hunter.picasso.complete(hunter);
              }
              break;
            }
    
            ···
          }
        }
      };
    

    最后for循环遍历batch

    hunter.picasso.complete(hunter);
    

    调用picasso的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) {
          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);
        }
    
        if (hasMultiple) {
          //noinspection ForLoopReplaceableByForEach
          for (int i = 0, n = joined.size(); i < n; i++) {
            Action join = joined.get(i);
            deliverAction(result, from, join);
          }
        }
    
        if (listener != null && exception != null) {
          listener.onImageLoadFailed(this, uri, exception);
        }
      }
    

    终于看到了complete方法,但是还没完,这里核心是走deliverAction方法

      private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
        if (action.isCancelled()) {
          return;
        }
        if (!action.willReplay()) {
          targetToAction.remove(action.getTarget());
        }
        if (result != null) {
          if (from == null) {
            throw new AssertionError("LoadedFrom cannot be null.");
          }
          action.complete(result, from);
          if (loggingEnabled) {
            log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
          }
        } else {
          action.error();
          if (loggingEnabled) {
            log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
          }
        }
      }
    
    action.complete(result, from);
    

    主要看action.complete(result, from);
    之前我们在把任务加入dispatcher的时候,new了一个ImageViewAction,ImageViewAction继承自Action


    image.png

    查看Action的继承,有很多子类
    我们最开始设置的是ImageView,我们看看ImageViewAction的complete实现

      ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
          int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
          Callback callback, boolean noFade) {
        super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
            tag, noFade);
        this.callback = callback;
      }
    
      @Override 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));
        }
    
        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();
        }
      }
    

    重点看这一句

    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    
      static void setBitmap(ImageView target, Context context, Bitmap bitmap,
          Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof AnimationDrawable) {
          ((AnimationDrawable) placeholder).stop();
        }
        PicassoDrawable drawable =
            new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
        target.setImageDrawable(drawable);
      }
    
    target.setImageDrawable(drawable);
    

    这个target就是我们的ImageView

    好了,到这里,我们终于把我们的Bitmap转换成PicassoDrawable,PicassoDrawable继承自BitmapDrawable,最后塞给ImageView了,
    如果这里不是ImageView,流程也都大体相似,


    image.png

    如果我们在调用picasso的into方法时,传入的时一个target实现类,最终会通过调用Target的onBitmaoLoaded方法完成图片的加载

    相关文章

      网友评论

          本文标题:Android图片加载框架 Picasso 源码解析(with-

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