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()方法调用之前。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容