[关闭]
@ZeroGeek 2015-08-28T01:08:11.000000Z 字数 10701 阅读 722

Picasso开源库解析

android 基础知识


Picasso 是 Square 开源的图片缓存库,主要特点有:

基本使用

  1. Picasso.with(this) //Context
  2. .load("http://7xjung.com1.z0.glb.clouddn.com/randomview.png") //图片地址
  3. .placeholder(R.drawable.abc_btn_check_material) //占位图(等待下载)
  4. .error(R.drawable.abc_btn_radio_material) //加载错误
  5. .resize(200,200) //设置显示大小
  6. .centerCrop() //自适应布局,减小内存
  7. .into(mLoadIv); //用来显示图片控件的ID

下载地址 : https://github.com/square/picasso

分析工作流程

分析的是第一次加载图片的情况,没有缓存。

Picasso.with(this):

  1. public static Picasso with(Context context) {
  2. if (singleton == null) {
  3. synchronized (Picasso.class) {
  4. if (singleton == null) {
  5. singleton = new Builder(context).build(); //获取实例对象
  6. }
  7. }
  8. }
  9. return singleton;
  10. }
  11. //这里使用了单例模式,而且是双重检测锁的方法。 另外还使用到了Builder模式,Picasso对象的创建非常复杂。

得到的是context.getApplicationContext();

  1. public Builder(Context context) {
  2. if (context == null) {
  3. throw new IllegalArgumentException("Context must not be null.");
  4. }
  5. this.context = context.getApplicationContext();
  6. }

Picasso的部分成员变量

看下build()方法,返回Picasso,初始化各种服务,下载器,缓存器,线程池,是否resize图片,调度器

  1. public Picasso build() {
  2. Context context = this.context;
  3. if (downloader == null) {
  4. downloader = Utils.createDefaultDownloader(context);
  5. }
  6. if (cache == null) {
  7. cache = new LruCache(context);
  8. }
  9. if (service == null) {
  10. service = new PicassoExecutorService();
  11. }
  12. if (transformer == null) {
  13. transformer = RequestTransformer.IDENTITY;
  14. }
  15. Stats stats = new Stats(cache);
  16. Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
  17. return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
  18. defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
  19. }
  20. }
  21. //这里先初始化Downloader,Cache(使用的LRU算法),PicassoExecutorService(这是Picasso自己实现的线程池),Transformer(当请求被submit时可以调用来修改请求)。

之后调用load(),有4个重载方法

  1. public RequestCreator load(String path) {
  2. if (path == null) {
  3. return new RequestCreator(this, null, 0);
  4. }
  5. if (path.trim().length() == 0) {
  6. throw new IllegalArgumentException("Path must not be empty.");
  7. }
  8. return load(Uri.parse(path)); //调用load(Uri uri)
  9. }
  10. public RequestCreator load(File file) {
  11. if (file == null) {
  12. return new RequestCreator(this, null, 0);
  13. }
  14. return load(Uri.fromFile(file)); //调用load(Uri uri)
  15. }
  16. public RequestCreator load(int resourceId) {
  17. if (resourceId == 0) {
  18. throw new IllegalArgumentException("Resource ID must not be zero.");
  19. }
  20. return new RequestCreator(this, null, resourceId);
  21. }
  22. public RequestCreator load(Uri uri) {
  23. return new RequestCreator(this, uri, 0);
  24. }

最终都是执行

new RequestCreator(this, uri, 0)

那么RequsetCreator是什么呢?(创造图片下载的请求)

private final Request.Builder data; //this.data

  1. RequestCreator(Picasso picasso, Uri uri, int resourceId) {
  2. if (picasso.shutdown) {
  3. throw new IllegalStateException(
  4. "Picasso instance already shut down. Cannot submit new requests.");
  5. }
  6. this.picasso = picasso;
  7. this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  8. }

Request类中的Builder()方法,初始化

  1. Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
  2. this.uri = uri;
  3. this.resourceId = resourceId;
  4. this.config = bitmapConfig;
  5. }

之后是into()

  1. public void into(ImageView target) {
  2. into(target, null);
  3. }

关键在于下面这个方法:

  1. public void into(ImageView target, Callback callback) {
  2. long started = System.nanoTime();
  3. checkMain(); //Looper.getMainLooper().getThread() == Thread.currentThread(),检查是不是主线程执行的。
  4. if (target == null) {
  5. throw new IllegalArgumentException("Target must not be null.");
  6. }
  7. if (!data.hasImage()) { //boolean hasImage() {return uri != null || resourceId != 0;},是否为空
  8. picasso.cancelRequest(target);
  9. if (setPlaceholder) {
  10. setPlaceholder(target, getPlaceholderDrawable());
  11. }
  12. return;
  13. }
  14. if (deferred) { //public RequestCreator fit() {deferred = true;return this;}判断是否改变图片大小,适配控件;deferred在这里是默认值false
  15. if (data.hasSize()) {
  16. throw new IllegalStateException("Fit cannot be used with resize.");
  17. }
  18. int width = target.getWidth();
  19. int height = target.getHeight();
  20. if (width == 0 || height == 0) {
  21. if (setPlaceholder) {
  22. setPlaceholder(target, getPlaceholderDrawable());
  23. }
  24. picasso.defer(target, new DeferredRequestCreator(this, target, callback));
  25. return;
  26. }
  27. data.resize(width, height);
  28. }
  29. Request request = createRequest(started); //这里创建了请求,给请求唯一的id,url,并且转换request
  30. String requestKey = createKey(request); //给请求创建了一个key
  31. if (shouldReadFromMemoryCache(memoryPolicy)) { //读取缓存
  32. Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
  33. if (bitmap != null) {
  34. picasso.cancelRequest(target);//如果cache里能找到,就取消请求,直接设置
  35. setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
  36. if (picasso.loggingEnabled) {
  37. log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
  38. }
  39. if (callback != null) {
  40. callback.onSuccess();
  41. }
  42. return;
  43. }
  44. }
  45. if (setPlaceholder) {
  46. setPlaceholder(target, getPlaceholderDrawable());
  47. }
  48. Action action = //Action是一个抽象类
  49. new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
  50. errorDrawable, requestKey, tag, callback, noFade);
  51. picasso.enqueueAndSubmit(action);
  52. }

创建回调方法:下载完图片,再通过控件显示

  1. ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
  2. int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
  3. Callback callback, boolean noFade) {
  4. super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
  5. tag, noFade);
  6. this.callback = callback;
  7. }

Action构造方法

  1. Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
  2. int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
  3. this.picasso = picasso;
  4. this.request = request;
  5. this.target =
  6. target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
  7. this.memoryPolicy = memoryPolicy;
  8. this.networkPolicy = networkPolicy;
  9. this.noFade = noFade;
  10. this.errorResId = errorResId;
  11. this.errorDrawable = errorDrawable;
  12. this.key = key;
  13. this.tag = (tag != null ? tag : this);
  14. }
  15. // Action代表了一个具体的加载任务

Action中用了一个弱引用,创建请求队列

  1. static class RequestWeakReference<M> extends WeakReference<M> {
  2. final Action action;
  3. public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {
  4. super(referent, q);
  5. this.action = action;
  6. }
  7. }

最后调用的一个方法:picasso.enqueueAndSubmit(action);

  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); // final Map<Object, Action> targetToAction;映射结构
  7. }
  8. submit(action);
  9. }

分给调度器执行任务(分发任务)

final Dispatcher dispatcher;

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

发送到消息队列

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

看下handler如何工作的?

  1. //Dispatcher构造方法的语句
  2. this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);

定义了一个静态内部类,执行不同的请求:

  1. private static class DispatcherHandler extends Handler {
  2. private final Dispatcher dispatcher;
  3. public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
  4. super(looper);
  5. this.dispatcher = dispatcher;
  6. }
  7. @Override public void handleMessage(final Message msg) {
  8. switch (msg.what) {
  9. case REQUEST_SUBMIT: {
  10. Action action = (Action) msg.obj;
  11. dispatcher.performSubmit(action);
  12. break;
  13. }
  14. ......//省略其它case
  15. }
  16. }
  17. }

请求REQUEST_SUBMIT之后的方法:

  1. void performSubmit(Action action) {
  2. performSubmit(action, true);
  3. }
  4. void performSubmit(Action action, boolean dismissFailed) {
  5. if (pausedTags.contains(action.getTag())) {
  6. pausedActions.put(action.getTarget(), action); // final Map<Object, Action> pausedActions;
  7. if (action.getPicasso().loggingEnabled) {
  8. log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
  9. "because tag '" + action.getTag() + "' is paused");
  10. }
  11. return;
  12. }
  13. BitmapHunter hunter = hunterMap.get(action.getKey()); //BitmapHunter实现了Runnable方法
  14. if (hunter != null) {
  15. hunter.attach(action); //注意这个attach(),将action加入到一个List中,actions.add(action);
  16. return;
  17. }
  18. if (service.isShutdown()) {
  19. if (action.getPicasso().loggingEnabled) {
  20. log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
  21. }
  22. return;
  23. }
  24. hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  25. hunter.future = service.submit(hunter); //交给线程池去执行, 初始化的PicassoExecutorService, <T> Future<T> submit(Runnable task, T result);
  26. hunterMap.put(action.getKey(), hunter); //管理起来,防止任务重复
  27. if (dismissFailed) {
  28. failedActions.remove(action.getTarget());
  29. }
  30. if (action.getPicasso().loggingEnabled) {
  31. log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
  32. }
  33. }
  34. //线程池中结合优先级去执行任务的,之前有设定Picasso类中的枚举类型Priority。

终于找到了图片加载的来源,就是BitmapHunter的run()执行的:

result = hunt()

下面是hunt()方法

  1. Bitmap hunt() throws IOException {
  2. Bitmap bitmap = null;
  3. if (shouldReadFromMemoryCache(memoryPolicy)) {
  4. bitmap = cache.get(key);
  5. if (bitmap != null) {
  6. stats.dispatchCacheHit();
  7. loadedFrom = MEMORY;
  8. if (picasso.loggingEnabled) {
  9. log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
  10. }
  11. return bitmap;
  12. }
  13. }
  14. data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
  15. RequestHandler.Result result = requestHandler.load(data, networkPolicy);
  16. if (result != null) {
  17. loadedFrom = result.getLoadedFrom();
  18. exifRotation = result.getExifOrientation();
  19. bitmap = result.getBitmap();
  20. // If there was no Bitmap then we need to decode it from the stream.
  21. if (bitmap == null) {
  22. InputStream is = result.getStream();
  23. try {
  24. bitmap = decodeStream(is, data);
  25. } finally {
  26. Utils.closeQuietly(is);
  27. }
  28. }
  29. }
  30. if (bitmap != null) {
  31. if (picasso.loggingEnabled) {
  32. log(OWNER_HUNTER, VERB_DECODED, data.logId());
  33. }
  34. stats.dispatchBitmapDecoded(bitmap);
  35. if (data.needsTransformation() || exifRotation != 0) {
  36. synchronized (DECODE_LOCK) {
  37. if (data.needsMatrixTransform() || exifRotation != 0) {
  38. bitmap = transformResult(data, bitmap, exifRotation);
  39. if (picasso.loggingEnabled) {
  40. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
  41. }
  42. }
  43. if (data.hasCustomTransformations()) {
  44. bitmap = applyCustomTransformations(data.transformations, bitmap);
  45. if (picasso.loggingEnabled) {
  46. log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
  47. }
  48. }
  49. }
  50. if (bitmap != null) {
  51. stats.dispatchBitmapTransformed(bitmap);
  52. }
  53. }
  54. }
  55. return bitmap;
  56. }

Picasso的Handler来处理接受到的消息(主线程中)

  1. static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
  2. @Override public void handleMessage(Message msg) {
  3. switch (msg.what) {
  4. case HUNTER_BATCH_COMPLETE: {
  5. @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
  6. //noinspection ForLoopReplaceableByForEach
  7. for (int i = 0, n = batch.size(); i < n; i++) {
  8. BitmapHunter hunter = batch.get(i);
  9. hunter.picasso.complete(hunter); //这里获取到result
  10. }
  11. break;
  12. }
  13. case REQUEST_GCED: {
  14. Action action = (Action) msg.obj;
  15. if (action.getPicasso().loggingEnabled) {
  16. log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), "target got garbage collected");
  17. }
  18. action.picasso.cancelExistingRequest(action.getTarget());
  19. break;
  20. }
  21. case REQUEST_BATCH_RESUME:
  22. @SuppressWarnings("unchecked") List<Action> batch = (List<Action>) msg.obj;
  23. //noinspection ForLoopReplaceableByForEach
  24. for (int i = 0, n = batch.size(); i < n; i++) {
  25. Action action = batch.get(i);
  26. action.picasso.resumeAction(action);
  27. }
  28. break;
  29. default:
  30. throw new AssertionError("Unknown handler message received: " + msg.what);
  31. }
  32. }
  33. };

完成后是调用的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. ImageView target = this.target.get();
  7. if (target == null) {
  8. return;
  9. }
  10. Context context = picasso.context;
  11. boolean indicatorsEnabled = picasso.indicatorsEnabled;
  12. //这里来设置图片
  13. PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
  14. if (callback != null) {
  15. callback.onSuccess(); //调用回调方法
  16. }
  17. }

//关于线程池的分析又是一大波。。。暂时不贴上

主要涉及的类:Picasso,Request,RequestCreator, Action, ImageViewAction, Utils,Dispatcher, BitmapHunter...

整体架构分析

参考

http://www.cnphp6.com/archives/87269

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注