手记

(Android)Picasso图片处理框架简要分析 2

接上篇(Android)Picasso图片处理框架简要分析 1

上篇分析到Picasso的into方法,在创建action后,会调用

[java] view plain copy

  1. Action action =  

  2.         new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,  

  3.             errorDrawable, requestKey, tag, callback, noFade);  

  4.   

  5.     picasso.enqueueAndSubmit(action);  

这里把创建好的action进行提交

[html] view plain copy

  1. void enqueueAndSubmit(Action action) {  

  2.     Object target = action.getTarget();  

  3.     if (target != null && targetToAction.get(target) != action) {  

  4.       // This will also check we are on the main thread.  

  5.       cancelExistingRequest(target);  

  6.       targetToAction.put(target, action);  

  7.     }  

  8.     submit(action);  

  9.   }  

  10.   

  11.   void submit(Action action) {  

  12.     dispatcher.dispatchSubmit(action);  

  13.   }  

Dispatcher

Dispatcher是Picasso里非常重要的一个类,在这里面完成了子线程到主线程得到切换

去dispatcher类看看这个方法,使用handler发送一个消息到dispatcherThread线程,这个是一个继承于HandlerThread,这个大家都熟悉吧,是自带lopper的thread

[java] view plain copy

  1. void dispatchSubmit(Action action) {  

  2.     handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));  

  3.   }  

这个handler是个DispatcherHandler,是dispatcher里的一个静态内部类,这是dispatcher里的子线程handler

[java] view plain copy

  1. DispatcherHandler(Looper looper, Dispatcher dispatcher) {  

  2.   super(looper);  

  3.   this.dispatcher = dispatcher;  

  4. }  

除此之外,还有一个主线程handler

[java] view plain copy

  1. this.mainThreadHandler = mainThreadHandler;  

这个handler实在Picasso类中传入的

[java] view plain copy

  1. static final Handler HANDLER = new Handler(Looper.getMainLooper())  

[java] view plain copy

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

ok,接着看dispatchSubmit,最终会调用到这里

  1. void performSubmit(Action action, boolean dismissFailed) {  

  2.     if (pausedTags.contains(action.getTag())) {  

  3.       pausedActions.put(action.getTarget(), action);  

  4.       if (action.getPicasso().loggingEnabled) {  

  5.         log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),  

  6.             "because tag '" + action.getTag() + "' is paused");  

  7.       }  

  8.       return;  

  9.     }  

  10.   

  11.     BitmapHunter hunter = hunterMap.get(action.getKey());  

  12.     if (hunter != null) {  

  13.       hunter.attach(action);  

  14.       return;  

  15.     }  

  16.   

  17.     if (service.isShutdown()) {  

  18.       if (action.getPicasso().loggingEnabled) {  

  19.         log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");  

  20.       }  

  21.       return;  

  22.     }  

  23.   

  24.     hunter = forRequest(action.getPicasso(), this, cache, stats, action);  

  25.     hunter.future = service.submit(hunter);  

  26.     hunterMap.put(action.getKey(), hunter);  

  27.     if (dismissFailed) {  

  28.       failedActions.remove(action.getTarget());  

  29.     }  

  30.   

  31.     if (action.getPicasso().loggingEnabled) {  

  32.       log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());  

  33.     }  

  34.   }  


首先检查这个action是否在pausedTags这个列表当中,pausedTags是一个set集合,用来记录暂停任务的列表。如它在列表中,则放入pausedActions列表中,以便到时唤醒请求。

如不在列表中,则创建BitmapHunter这个runnable,然后调用attach方法,将Action加入action list这个列表中,然后return

如hunterMap中没有这个hunter,会创建BitmapHunter,提交到到service线程池,然后将hunter放到hunterMap这个集合中。 线程池的执行过程,其实就是执行hunt方法,这在上篇文章中已经分析过了,就是命中缓存或者在NetworkRequestHandler调用okhttp下载图片(如果没有okhttp,则用UrlConnection)。


  1. Bitmap hunt() throws IOException {  

  2.    Bitmap bitmap = null;  

  3.   

  4.    if (shouldReadFromMemoryCache(memoryPolicy)) {  

  5.      bitmap = cache.get(key);  

  6.      if (bitmap != null) {  

  7.        stats.dispatchCacheHit();  

  8.        loadedFrom = MEMORY;  

  9.        if (picasso.loggingEnabled) {  

  10.          log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");  

  11.        }  

  12.        return bitmap;  

  13.      }  

  14.    }  

  15.   

  16.    networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;  

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

  18.    if (result != null) {  

  19.      loadedFrom = result.getLoadedFrom();  

  20.      exifOrientation = result.getExifOrientation();  

  21.      bitmap = result.getBitmap();  

  22.   

  23.      // If there was no Bitmap then we need to decode it from the stream.  

  24.      if (bitmap == null) {  

  25.        Source source = result.getSource();  

  26.        try {  

  27.          bitmap = decodeStream(source, data);  

  28.        } finally {  

  29.          try {  

  30.            //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.  

  31.            source.close();  

  32.          } catch (IOException ignored) {  

  33.          }  

  34.        }  

  35.      }  

  36.    }  

  37.   

  38.    if (bitmap != null) {  

  39.      if (picasso.loggingEnabled) {  

  40.        log(OWNER_HUNTER, VERB_DECODED, data.logId());  

  41.      }  

  42.      stats.dispatchBitmapDecoded(bitmap);  

  43.      if (data.needsTransformation() || exifOrientation != 0) {  

  44.        synchronized (DECODE_LOCK) {  

  45.          if (data.needsMatrixTransform() || exifOrientation != 0) {  

  46.            bitmap = transformResult(data, bitmap, exifOrientation);  

  47.            if (picasso.loggingEnabled) {  

  48.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());  

  49.            }  

  50.          }  

  51.          if (data.hasCustomTransformations()) {  

  52.            bitmap = applyCustomTransformations(data.transformations, bitmap);  

  53.            if (picasso.loggingEnabled) {  

  54.              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");  

  55.            }  

  56.          }  

  57.        }  

  58.        if (bitmap != null) {  

  59.          stats.dispatchBitmapTransformed(bitmap);  

  60.        }  

  61.      }  

  62.    }  

  63.   

  64.    return bitmap;  

  65.  }  


hunt方法执行完毕后,会返回Bitmap,在Bitmap Hunter的run方法中,会得到这个结果


  1. @Override public void run() {  

  2.     try {  

  3.       updateThreadName(data);  

  4.   

  5.       if (picasso.loggingEnabled) {  

  6.         log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));  

  7.       }  

  8.   

  9.       result = hunt();  

  10.   

  11.       if (result == null) {  

  12.         dispatcher.dispatchFailed(this);  

  13.       } else {  

  14.         dispatcher.dispatchComplete(this);  

  15.       }  

  16.     } catch (NetworkRequestHandler.ResponseException e) {  

  17.       if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {  

  18.         exception = e;  

  19.       }  

  20.       dispatcher.dispatchFailed(this);  

  21.     } catch (IOException e) {  

  22.       exception = e;  

  23.       dispatcher.dispatchRetry(this);  

  24.     } catch (OutOfMemoryError e) {  

  25.       StringWriter writer = new StringWriter();  

  26.       stats.createSnapshot().dump(new PrintWriter(writer));  

  27.       exception = new RuntimeException(writer.toString(), e);  

  28.       dispatcher.dispatchFailed(this);  

  29.     } catch (Exception e) {  

  30.       exception = e;  

  31.       dispatcher.dispatchFailed(this);  

  32.     } finally {  

  33.       Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);  

  34.     }  

  35.   }  

上述代码可以看到,会对result进行判断,然后走到dispatcher中的相应回调方法


  1. void dispatchComplete(BitmapHunter hunter) {  

  2.     handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));  

  3.   }  

看看这个handler做了什么


  1. case HUNTER_COMPLETE: {  

  2.          BitmapHunter hunter = (BitmapHunter) msg.obj;  

  3.          dispatcher.performComplete(hunter);  

  4.          break;  

  5.        }  

看performComplete


  1. void performComplete(BitmapHunter hunter) {  

  2.    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {  

  3.      cache.set(hunter.getKey(), hunter.getResult());  

  4.    }  

  5.    hunterMap.remove(hunter.getKey());  

  6.    batch(hunter);  

  7.    if (hunter.getPicasso().loggingEnabled) {  

  8.      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");  

  9.    }  

  10.  }  

首先,根据条件进行缓存写入,然后在hunterMap中remove这个hunter,然后调用batch方法


  1. private void batch(BitmapHunter hunter) {  

  2.   if (hunter.isCancelled()) {  

  3.     return;  

  4.   }  

  5.   if (hunter.result != null) {  

  6.     hunter.result.prepareToDraw();  

  7.   }  

  8.   batch.add(hunter);  

  9.   if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {  

  10.     handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);  

  11.   }  

  12. }  

我们关注下面,发了一个空消息给子线程handler


  1. case HUNTER_DELAY_NEXT_BATCH: {  

  2.           dispatcher.performBatchComplete();  

  3.           break;  

  4.         }  

然后继续看


  1. void performBatchComplete() {  

  2.   List<BitmapHunter> copy = new ArrayList<>(batch);  

  3.   batch.clear();  

  4.   mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));  

  5.   logBatch(copy);  

  6. }  

终于完成了,这边给主线程handler发送了消息,并且清除了batch list,这个handler在Picasso这个类中


  1. case HUNTER_BATCH_COMPLETE: {  

  2.          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;  

  3.          //noinspection ForLoopReplaceableByForEach  

  4.          for (int i = 0, n = batch.size(); i < n; i++) {  

  5.            BitmapHunter hunter = batch.get(i);  

  6.            hunter.picasso.complete(hunter);  

  7.          }  

  8.          break;  

  9.        }  

然后会调用到


  1. private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {  

  2.     if (action.isCancelled()) {  

  3.       return;  

  4.     }  

  5.     if (!action.willReplay()) {  

  6.       targetToAction.remove(action.getTarget());  

  7.     }  

  8.     if (result != null) {  

  9.       if (from == null) {  

  10.         throw new AssertionError("LoadedFrom cannot be null.");  

  11.       }  

  12.       action.complete(result, from);  

  13.       if (loggingEnabled) {  

  14.         log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);  

  15.       }  

  16.     } else {  

  17.       action.error(e);  

  18.       if (loggingEnabled) {  

  19.         log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());  

  20.       }  

  21.     }  

  22.   }  

执行了action的complete方法,可以在ImageViewAction看实现


  1. @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {  

  2.   if (result == null) {  

  3.     throw new AssertionError(  

  4.         String.format("Attempted to complete action with no result!\n%s", this));  

  5.   }  

  6.   

  7.   ImageView target = this.target.get();  

  8.   if (target == null) {  

  9.     return;  

  10.   }  

  11.   

  12.   Context context = picasso.context;  

  13.   boolean indicatorsEnabled = picasso.indicatorsEnabled;  

  14.   PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);  

  15.   

  16.   if (callback != null) {  

  17.     callback.onSuccess();  

  18.   }  

  19. }  

最后在PicassoDrawable对ImageView设置了图片,这样图片就设置完成了。


  1. static void setBitmap(ImageView target, Context context, Bitmap bitmap,  

  2.     Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {  

  3.   Drawable placeholder = target.getDrawable();  

  4.   if (placeholder instanceof Animatable) {  

  5.     ((Animatable) placeholder).stop();  

  6.   }  

  7.   PicassoDrawable drawable =  

  8.       new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);  

  9.   target.setImageDrawable(drawable);  

  10. }  

总结一下整个流程


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

1.load方法中会创建RequestCreator,设置图片的基本信息,placeholder,error等

2.into方法会在dispatcher中进行请求管理,测试缓存命中,没有命中则创建action,包装请求,然后提交action到线程池,然后创建hunter对象,它是一个runnable,在run方法中会调用hunt方法。在hunt方法中再次测试缓存,如未命中则去NetworkRequestHandler中进行网络请求下载图片。

3.得到图片后设置图片到Imageview

最后,盗图一张总结

原文出处





0人推荐
随时随地看视频
慕课网APP