Picasso源码完全解析(二)--Picasso实例的创建

Picasso源码完全解析(一)--概述

Picasso源码完全解析(二)--Picasso实例的创建

Picasso源码完全解析(三)--Request和Action的创建

Picasso源码完全解析(四)--Action分发和执行

Picasso源码完全解析(五)--图片的获取(BitmapHunter)

Picasso源码完全解析(六)--请求的取消、暂停、和恢复

Picasso源码完全解析(七)-- CleanupThread 取消请求

Picasso源码完全解析(二)--Picasso实例的创建

Picasso提供两种方式获得Picasso实例

全局的默认实例

通过Picasso.Builder自己构建Picasso实例

由于Picasso是一个重量级的对象,它的创建涉及到很多资源和复杂的过程,比如它需要占用一部分内存作为缓存,需要开启回收线程等等,同时其销毁也比较复杂,频繁的创建和销毁其实例会带来不必要的开销,因此,Picasso维护一个全局的实例用以满足大部分需求。当这个全局单例不能满足所有需求的时候,比如自定义图片的下载、处理以及转换等,可以根据需求实现自己的Picasso实例,使用完之后及时shutDown()该实例,节约系统资源。

由于Picasso的构造比较复杂,所以,Picasso的实例化比较适合用建造者模式。

下面详细介绍几方面

使用Picasso.Builder自己构建Picasso实例

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,

RequestTransformer requestTransformer, List extraRequestHandlers, Stats stats,

Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {

.......

}

Picasso没有对外暴露其构造函数,只能通过Picasso.Builder()建造者模式创建自己的实例。建造者模式将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。 按照《Effective Java》的总结: 当构造方法或者静态工厂方法中的参数过多的时候,尤其是可选参数很多时,考虑使用建造者模式。Picasso的构造器参数就比较多(10个),建造者模式使用的相当精妙。 我们可以看到Picasso的构造函数需要的参数特别多,因此很适合使用建造者模式。

private final Context context;

private Downloader downloader;

private ExecutorService service;

private Cache cache;

private Listener listener;

private RequestTransformer transformer;

private List requestHandlers;

private Bitmap.Config defaultBitmapConfig;

private boolean indicatorsEnabled;

private boolean loggingEnabled;

可以看到Builder对象需要10个属性,这10个属性和Picasso实例的10个属性是一一对应的,同时每一个属性都对应一个setter方法并返回this,用来实现链式调用。以Downloader的设置为例:

public Builder downloader(Downloader downloader) {

if (downloader == null) {

throw new IllegalArgumentException("Downloader must not be null.");

}

if (this.downloader != null) {

throw new IllegalStateException("Downloader already set.");

}

this.downloader = downloader;

return this;

}

首先对空指针和重复设置进行检查,检查通过后对属性进行赋值,最后返回this引用。 通过方法参数对下载器进行了依赖注入,参数的类型是Downloader接口而不是具体的实现,体现了面向接口编程的思想,这样Picasso避免了和某一个具体下载器的耦合关系,并且方便用户定义自己的下载器,只要定义的下载器实现了Downloader接口并覆写了相应的方法即可,从而使Picasso非常的灵活和方便扩展。其他几个属性的setter类似,不再赘述。 这样通过一些列的setter配置属性后,最终通过build方法创建实例

下面看看build()方法

//2.5.2版本以前

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

}

}

//2.5.2版本

public Picasso build() {

Context context = this.context;

if (downloader == null) {

downloader = new OkHttp3Downloader(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);

}

可以看到默认情况下,Picasso使用LRUCache作为内存缓存,使用PicassoExecutorService线程池。

先看看PicassoExecutorService

class PicassoExecutorService extends ThreadPoolExecutor {

private static final int DEFAULT_THREAD_COUNT = 3;

PicassoExecutorService() {

super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,

new PriorityBlockingQueue(), new Utils.PicassoThreadFactory());

}

void adjustThreadCount(NetworkInfo info) {

if (info == null || !info.isConnectedOrConnecting()) {

setThreadCount(DEFAULT_THREAD_COUNT);

return;

}

switch (info.getType()) {

case ConnectivityManager.TYPE_WIFI:

case ConnectivityManager.TYPE_WIMAX:

case ConnectivityManager.TYPE_ETHERNET:

setThreadCount(4);

break;

case ConnectivityManager.TYPE_MOBILE:

switch (info.getSubtype()) {

case TelephonyManager.NETWORK_TYPE_LTE:  // 4G

case TelephonyManager.NETWORK_TYPE_HSPAP:

case TelephonyManager.NETWORK_TYPE_EHRPD:

setThreadCount(3);

break;

case TelephonyManager.NETWORK_TYPE_UMTS: // 3G

case TelephonyManager.NETWORK_TYPE_CDMA:

case TelephonyManager.NETWORK_TYPE_EVDO_0:

case TelephonyManager.NETWORK_TYPE_EVDO_A:

case TelephonyManager.NETWORK_TYPE_EVDO_B:

setThreadCount(2);

break;

case TelephonyManager.NETWORK_TYPE_GPRS: // 2G

case TelephonyManager.NETWORK_TYPE_EDGE:

setThreadCount(1);

break;

default:

setThreadCount(DEFAULT_THREAD_COUNT);

}

break;

default:

setThreadCount(DEFAULT_THREAD_COUNT);

}

}

可以看到,PicassoExecutorService默认使用3个线程作为下载线程,同时可以根据网络状况调整工作线程的数量。

2.5.2版本开始,Picasso使用OkHttp3进行下载,磁盘缓存交给OkHttp实现,2.5.2以前有所不一样,看看Utils.createDefaultDownloader(context):

static Downloader createDefaultDownloader(Context context) {

if (SDK_INT >= GINGERBREAD) {

try {

Class.forName("okhttp3.OkHttpClient");

return OkHttp3DownloaderCreator.create(context);

} catch (ClassNotFoundException ignored) {

}

try {

Class.forName("com.squareup.okhttp.OkHttpClient");

return OkHttpDownloaderCreator.create(context);

} catch (ClassNotFoundException ignored) {

}

}

return new UrlConnectionDownloader(context);

}

可以看出,2.5.2之前,如果应用集成了OkHttp,那么就使用OkHttp进行下载,磁盘缓存交给OkHttp实现,如果没有集成,就使用UrlConnectionDownloader进行下载。

Picasso没有实现磁盘缓存,磁盘缓存交给DowLoader实现。

使用默认的全局Picasso实例

通过with()方法,可以获取全局的Picasso实例:

public static Picasso with(@NonNull Context context) {

if (context == null) {

throw new IllegalArgumentException("context == null");

}

if (singleton == null) {

synchronized (Picasso.class) {

if (singleton == null) {

singleton = new Builder(context).build();

}

}

}

return singleton;

}

Picasso使用标准的双检锁构建线程安全的全局实例,注意静态属性singleton使用volatile修饰保证多线程对singleton的可见性。

可以看出,默认情况下全局实例使用的是默认配置,大多数情况下,该实例能够满足绝大多数的需求,如果不能,可以通过自己实例化Picasso实例并通过setSingletonInstance()设置为单例,但是该方法的调用一定要在所有with()方法调用之前。

你可能感兴趣的:(Picasso源码完全解析(二)--Picasso实例的创建)