Android进化徽章-Picasso

这几天看了许多大神写的源码解析的文章,感觉根本看不懂啊!心力憔悴啊有没有!
这里写图片描述

于是乎决定自己来分析一遍源码,可能分析得不对或者太浅,但我觉得也会比光看别人的分析会有价值的多!


好的,咱们先用鼎鼎大名的Picasso开刀!

Picasso的简单用法:

        Picasso.with(this).load(R.mipmap.ic_launcher).into(iv);// iv是一个imageView的引用对象

OK,让我们一步一步得看下picasso的工作原理!

首先观察with()方法;

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

singleton是一个Picasso类的实例,可以看到这里使用单例模式来创建实例;
// todo 这是哪种单例模式呢?

单例模式中又使用了Builder模式来构建实例,让我们往下看它具体做了哪些操作。

 public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(context); // 初始化下载器
      }
      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);
    }

咱们可以看到在build()方法里初始化了一堆对象,下载器、缓存、服务等;然后利用这些对象去构建Picasso实例,嗯,这非常符合builder模式的使用方式;
最终是创建了一个Picasso实例;

小结:with()方法主要是初始化一系列必须要用到的对象,例如下载器,缓存等,返回的是一个Picasso实例;


咱们接着往下看load()方法具体都干了啥!
这里写图片描述

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

这个方法返回了一个RequestCreator对象,其构造方法利用了资源文件id,让咱们接着往下看;

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

构造方法总共就干了2件事;
1.让RequestCreator持有picasso实例;
2.初始化data对象;


好了,最后就来到into()方法了,让咱们终极这次窥探之旅吧!
这里写图片描述

public void into(ImageView target, Callback callback) {
    long started = System.nanoTime(); // 获得当前的时间
    checkMain(); // 检查是否处于UI线程,若不是则会抛出异常

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }

     // 假如data不持有资源文件id,则取消这次请求;
    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); // 获取一个key

    if (shouldReadFromMemoryCache(memoryPolicy)) { // 如果要从缓存中获取图片的话
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); // 根据这个key去获取缓存中的图片
      if (bitmap != null) {
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled); // 这里将从缓存中获取的图片载入target,即咱们上面的iv,此时图片就出来啦!
        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对象插入到队列中并提交
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);

    picasso.enqueueAndSubmit(action);
  }

咱们可以从中大致了解到picasso的图片加载策略;
1.先判断能否正常加载资源,若不能则再判断有无设置加载等待图片,若有则显示等待图片;
2.判断是否应该从缓存中获取图片,若是则根据key来获取图片资源;若获取成功则取消请求,利用从缓存中获取的图片载入到target中。若获取失败则继续往下走;
3.如果设置了等待图片,则先载入等待图片用作过渡;
4.利用上面的一堆变量来构建一个action对象,并将这个action对象插入队列中;(则证明这个操作是异步的?所以才会出现第2个图片会比第1个图片更早显示的情况)


还没完呀!小编我第一次写这么长的文章啊!
这里写图片描述

让我们继续看下这个action对象是什么,和插入队列后具体都干了啥!

首先是action对象究竟是什么鬼!

abstract class Action<T> {

····
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
      int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
    this.picasso = picasso;
    this.request = request;
    this.target =
        target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
    this.memoryPolicy = memoryPolicy;
    this.networkPolicy = networkPolicy;
    this.noFade = noFade;
    this.errorResId = errorResId;
    this.errorDrawable = errorDrawable;
    this.key = key;
    this.tag = (tag != null ? tag : this);
  }
  ····
  }

action对象是一个抽象类,它持有了诸多的变量,如picasso,请求,target,key之类的,几乎所有有用到的变量都被它持有了!拿这么“资源”究竟是想搞什么大动作啊!
让我们往下瞧这个action插入队列后都干了什么;

void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
      // This will also check we are on the main thread.
      cancelExistingRequest(target);
      targetToAction.put(target, action);
    }
    submit(action);
  }

这里target也是咱们上面的iv,而这个targetToAction是在picasso对象初始化的时候创建的。targetToAction在这之前并未做任何put操作,所以第一次执行时肯定会进入判断,并且将target和action做关联保存起来;最后执行submit();

看起来这是最后的方法了!
这里写图片描述

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

额。。。怎么出来了个dispatcher,这又是什么鬼?

看下dispatchSubmit做了什么

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

实际上把这个action发送给了handler,让handler处理,那么最终的操作是真的要来了!
这里写图片描述

  ····
@Override public void handleMessage(final Message msg) {
      switch (msg.what) {
        case REQUEST_SUBMIT: {
          Action action = (Action) msg.obj;
          dispatcher.performSubmit(action);
          break;
        }
  }
   ····
 void performSubmit(Action action) {
    performSubmit(action, true);
  }
 void performSubmit(Action action, boolean dismissFailed) {
    ····
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    hunter.future = service.submit(hunter);
  ····

已经可以看到关键的部分了,service.submit(hunter);

好的,咱们马上就成功了!继续往下走!

Future<?> submit(Runnable task);

额。。。service是一个接口,好吧,让我们回头瞧瞧这个service在哪里初始化的。一直往回滚可以看到在build()方法中初始化的service,具体的实现为

  @Override
  public Future<?> submit(Runnable task) {
    PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
    execute(ftask);
    return ftask;
  }

到此就结束了,execute里面不再分析(实际上是看不懂);


总结:
这篇文章只是超级简单的追溯了一下关键的方法,很浅的观察了一下内部实现;从中能简单的分析出picasso的加载策略(文章中唯一有价值的东西?)。小编认为picasso中的下载器,缓存实现都是非常值得去研究的知识点。下次再来分析咯~

// TODO 下载器分析,缓存分析


题外话:
小编是看了许多大神的源码分析后感觉很多地方看不懂,而且也不知道能学到什么知识点;因此衍生出自己看源码或许更能学习到东西的想法,所以才会有这篇博文,在这小编希望各位大神能分享点学习的经验和思路,指出不足并留下宝贵的建议,感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值