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

16 篇文章 0 订阅
10 篇文章 0 订阅


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

  1. 全局的默认实例
  2. 通过Picasso.Builder自己构建Picasso实例

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

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

下面详细介绍几方面

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

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
        RequestTransformer requestTransformer, List<RequestHandler> 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<RequestHandler> 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<Runnable>(), 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()方法调用之前。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值