Picasso

Posted 劲火星空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Picasso相关的知识,希望对你有一定的参考价值。

一、例子



直接上代码

如下就是Picasso最简单的例子,我们在使用的时候就是这么简单,直接with、load、into

        // 普通加载图片
        Picasso.with(PicassoActivity.this)
                .load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
                .into(ivPicassoResult1);

        // 裁剪的方式加载图片
        Picasso.with(PicassoActivity.this)
                .load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
                .resize(100,100)
                .into(ivPicassoResult2);

        // 选择180度
        Picasso.with(PicassoActivity.this)
                .load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
                .rotate(180)
                .into(ivPicassoResult3);


二、原理

首先来看三个函数,第一个是with函数,很显然使用的是单例模式和Builder模式,然后创建一个Picasso的实例

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

然后load函数,这个有好几个,分别针对的是不同的资源类型

public RequestCreator load(Uri uri) 
    ...

public RequestCreator load(String path) 
    ...

public RequestCreator load(int resourceId) 
    ...


最后是into函数,这个函数还是比较复杂的一个函数

public void into(Target target) 
    long started = System.nanoTime();
    checkMain();

    if (target == null) 
        throw new IllegalArgumentException("Target must not be null.");
    
    if (deferred) 
        throw new IllegalStateException("Fit cannot be used with a Target.");
    

    if (!data.hasImage()) 
        picasso.cancelRequest(target);
        target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);
        return;
    

    Request request = createRequest(started);
    String requestKey = createKey(request);

    if (shouldReadFromMemoryCache(memoryPolicy)) 
        Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
        if (bitmap != null) 
            picasso.cancelRequest(target);
            target.onBitmapLoaded(bitmap, MEMORY);
            return;
        
    

    target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);

    Action action =
        new TargetAction(picasso, target, request, memoryPolicy, networkPolicy, errorDrawable,
            requestKey, tag, errorResId);
    picasso.enqueueAndSubmit(action);
这里最主要的就是创建一个Reques对象,我们前面做的一些封装和设置都会封装到这个Request对象中

检查我们要显示的图片是否可以直接在缓存中获取,如果有就直接显示出来好了。

缓存没命中,那就只能费点事把源图片down下来了。这个过程是异步的,并且通过一个Action来完成请求前后的衔接工作。


首先进行参数的设置,然后创建request请求对象,最后通过通过action来进行图片的请求。



三、深入

1. 首先看一下构造函数

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) 
    ...
我们是通过单例和建造者模式来完成实例化的,在build的过程中向picasso中传递这些参数,自己来灵活的定制


2、看一build函数中的代码是什么样子

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


Downloader:它是一个接口,规定了一些通用的方法,这也就意味着,我们可以提供自己的下载器

Cache:Picasso的缓存,这里实例化的是LruCache,其内部使用的是LinkedHashMap

ExecutorService:这里Picasso实现了自己的PicassoExecutorService,它继承了ThreadPoolExecutor,也就是Picasso自己维护了一个线程池,用于异步加载图片。

Stats:这个类只要是维护图片的一些状态Dispatcher:从名字上就可以判断出来,这个类在这里起到了一个调度器的作用,图片要不要开始下载以及下载后Bitmap的返回都是通过这个调度器来执行的


3、也是通过异步请求的方式来进行的

上面的into方法中中最终会创建一个action,这个action里边包含picasso对象、目标和Request请求等

然后会调用enqueueAndSubmit方法,而最终是调用了Dispatcher的dispatchSubmit方法,也就是我们前面说的,Dispatcher起到了调度器的作用。在Dispatcher内部,Dispatcher定义了DispatcherThread和DispatcherHandler两个内部类,并在Dispatcher的构造函数中对他们经行了实例化,所有的调度也都是通过handler异步的执行的,如下是Dispatcher

Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) 
    this.dispatcherThread = new DispatcherThread();
    this.dispatcherThread.start();
    ...
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    ...
构建实例之后建立一个 BitmapHunter类来进行图片的加载,这个类也是继承Runnable接口的类,加载完成图片之后怎么去进行进行主线程的更新是个问题


4、进行图片的主线程跟新操作

因为是异步的,最终也是通过消息机制来进行发送的,同message的方式发送到主线程中进行图片的渲染和更新的操作

Picasso并不是立即将图片显示出来,而是用到了一个批处理,其实就是把操作先暂存在一个list中,等空闲的时候再拿出来处理,这样做得好处也是尽量减少主线程的执行时间,一方面防止ANR,另一方面快速返回,响应页面的其他渲染操作,防止卡顿用户界面。

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





尊重作者,尊重原创,参考文章:


http://www.jianshu.com/p/459c8ca3f337






以上是关于Picasso的主要内容,如果未能解决你的问题,请参考以下文章

Picasso源码解析 一

Picasso

Picasso

Android图片加载框架Picasso最全使用教程 五

Android图片加载框架Picasso最全使用教程 五

Picasso源码解析