Picasso

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jinhuoxingkong/article/details/75452212

一、例子



直接上代码

如下就是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






阅读更多
换一批

没有更多推荐了,返回首页