Android:图片加载框架Picasso源码分析(Picasso 2.71828)

本文深入分析了新版 Picasso 2.71828 的源码实现,包括 Picasso 实例的获取、load 方法、RequestCreator 的 into 方法以及 Action 的提交和处理。通过双重检查锁模式和 Builder 设计模式,Picasso 提供了简洁的图片加载接口。重点讨论了 Context 的获取、Dispatcher 的工作原理以及 BitmapHunter 如何获取和处理 Bitmap。
摘要由CSDN通过智能技术生成

Picasso 是 Android 开发中最受欢迎的图片请求加载框架之一 ,它诞生于 2013 年,距今已有六年的生命。在这六年间 Picasso 发布过 21 个版本更新,而最近的一次更新为18年的 3 月 8 日,更新的版本号为 2.71828(文中统称为新版),该版本离上一次发布更新相隔了三年。本文主要分析新版 Picasso 的源码实现和它的一些 API 变化。

本文过长,推荐关注我之后再慢慢品尝~

1. 新版 Picasso 的使用

新版 Picasso 最直观的变化就是在 App 中的调用方式为:

Picasso.get().load(url).into(imageView);

该调用跟原来版本的调用区别是,没有了需要传 Context 的 with 方法,取而代之是一个不需要传参的 get 方法来获取全局唯一 Picasso 实例。

本文也主要通过分析 Picasso.get().load(url).into(imageView) 该句调用的来龙去脉来理清新版 Picasso 框架的实现原理。

2. Picasso 实例的获取

Picasso 实例是通过调用 Picasso 类中的静态方法 get 获取的,该方法也是新版 Picasso 的入口,我们从该方法开始看起:

static volatile Picasso singleton = null;

public static Picasso get() {
  if (singleton == null) {
    synchronized (Picasso.class) {
      if (singleton == null) {
        if (PicassoProvider.context == null) {
          throw new IllegalStateException("context == null");
        }
        singleton = new Builder(PicassoProvider.context).build();
      }
    }
  }
  return singleton;
}

上面这段代码是一个非常经典的双重检查锁模式 (Double Checked Locking Pattern)。

首先进入 get 方法即检查一次 Picasso 实例 singleton 是否为 null,不为 null 就可以直接返回该实例。

接着进入到同步块,因为可能会一个线程进入同步块后创建完对象后退出,另一个线程又紧接着进入同步块,因此进入同步块后需要再检查一次 singleton 是否为 null,如果还为 null,这时候可以开始初始化该实例。

最后静态变量 singleton 需要用 volatile 关键字来修饰,目的是为了防止重排序。

2.1 使用 ContentProvider 获取 Context

新版 Picasso 提供给我们的入口方法 get 不需要传 Context,因为它使用的是 PicassoProvider 类中的 context,我们看一下 PicassoProvider 类:

public final class PicassoProvider extends ContentProvider {

  @SuppressLint("StaticFieldLeak") static Context context;

  @Override public boolean onCreate() {
    context = getContext();
    return true;
  }

}

PicassoProvider 类继承自 ContentProvider,除了 onCreate 方法,其他方法都是默认实现 (为了节省篇幅,省略了该部分代码),而 onCreate 方法也只是调用 getContext 方法并赋值给静态变量 context,然后返回 true 表示成功加载了该 ContentProvider。

Picasso 这么做的理由是,只要将 PicassoProvider 在 AndroidManifest 文件中注册,那么 App 在启动的时候,系统就会自动回调 PicassoProvider 的 onCreate 方法,因此也就自动获取到了 Context。

2.2 Picasso 实例的创建

接着回到 Picasso.get 方法中,Picasso 实例通过该句代码创建:

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

这里使用到了常用的 Builder 设计模式。当一个类的属性过多,通过构造函数构造一个对象过于复杂时,可以选择使用 Builder 设计模式来简化对象的构造过程。

看下 Picasso 类中静态内部类 Builder 的构造函数:

public Builder(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("Context must not be null.");
  }
  this.context = context.getApplicationContext();
}

该构造函数确保传进来的 Context 实例不为 null,然后获取全局的 Application Context。

接着是 Picasso.Builder 类中的 build 方法:

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;

public Picasso build() {
  Context context = this.context;
  // 配置下载器 Downloader,用于从网络下载图片资源,默认为 OkHttp3Downloader
  if (downloader == null) {
    downloader = new OkHttp3Downloader(context);
  }
  // 配置缓存 Cache,用来保存最近查看使用的图片,默认为 LruCache
  if (cache == null) {
    cache = new LruCache(context);
  }
  // 配置 ExecutorService,默认为 PicassoExecutorService
  // 后面 Bitmap 的获取任务就在该线程池中完成
  if (service == null) {
    service = new PicassoExecutorService();
  }
  // 配置 RequestTransformer 实例
  if (transformer == null) {
    transformer = RequestTransformer.IDENTITY;
  }
  // 创建 Stats 实例,Stats 类用来进行一些统计,如缓存命中数,图片下载数等
  Stats stats = new Stats(cache);
  // 创建 Dispatcher 实例,Dispatcher 类顾名思义,它的作用就是用来分发处理
  // 各种图片操作事件的如提交图片请求事件,图片获取完成事件等;
  // 传入前面配置好的对象和 HANDLER 实例给 Dispatcher 类构造函数
  // 该 HANDLER 在主线程接收处理事件,后面获取到 Bitmap 后需要回调到
  // 该 HANDLER 的 handleMessage 方法中以便将 Bitmap 切换回主线程显示
  Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
  // 传入前面配置好的一系列参数,创建 Picasso 实例
  return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
      defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}

该方法的逻辑与相关类作用已在注释中进行了说明。

接下来看 Picasso 类的构造函数:

private final Listener listener;
private final RequestTransformer requestTransformer;
private final CleanupThread cleanupThread;
private final List<RequestHandler> requestHandlers;

final Context context;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final Map<Object, Action> targetToAction;
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
final ReferenceQueue<Object> referenceQueue;
final Bitmap.Config defaultBitmapConfig;

boolean indicatorsEnabled;
volatile boolean loggingEnabled;

boolean shutdown;

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
  // 一些赋值操作
  this.context = context;
  this.dispatcher = dispatcher;
  this.cache = cache;
  this.listener = listener;
  this.requestTransformer = requestTransformer;
  this.defaultBitmapConfig = defaultBitmapConfig;
  // Picasso 默认包含七个内置 RequestHandler 分别用来处理七种不同类型的请求
  // 你也可以自己继承 RequestHandler 类来处理你的自定义请求
  // 自定义请求放在 extraRequestHandlers 中
  int builtInHandlers = 7;
  int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
  List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);
  // 添加 ResourceRequestHandler,用于处理加载图片资源 id 的情况
  // ResourceRequestHandler 需要第一个进行添加
  // 避免其他的 RequestHandler 检查 (request.resourceId != 0) 的情况
  allRequestHandlers.add(new ResourceRequestHandler(context));
  // 然后添加自定义的 RequestHandler (如果有的话)
  if (extraRequestHandlers != null) {
    allRequestHandlers.addAll(extraRequestHandlers);
  }
  // 添加 ContactsPhotoRequestHandler,用于处理手机联系人图片
  allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
  // 添加 MediaStoreRequestHandler,用于处理 content://media/ 开头的 URI
  allRequestHandlers.add(new MediaStoreRequestHandler(context));
  // 添加 ContentStreamRequestHandler,用于处理 scheme 为 content 的 URI
  allRequestHandlers.add(new ContentStreamRequestHandler(context));
  // 添加 AssetRequestHandler,用于处理 file:///android_asset/ 开头的 URI
  allRequestHandlers.add(new AssetRequestHandler(context));
  // 添加 FileRequestHandler,用于处理 scheme 为 file 的 URI
  allRequestHandlers.add(new FileRequestHandler(context));
  // 添加 NetworkReques
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你在使用 Android Studio 中进行开发,可以尝试在 `build.gradle` 文件中添加以下依赖项: ``` dependencies { implementation 'com.android.support:support-v4:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.android.support:palette-v7:28.0.0' implementation 'com.android.support:support-vector-drawable:28.0.0' implementation 'com.android.support:animated-vector-drawable:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:support-annotations:28.0.0' implementation 'com.android.support:support-compat:28.0.0' implementation 'com.android.support:support-core-ui:28.0.0' implementation 'com.android.support:support-core-utils:28.0.0' implementation 'com.android.support:support-fragment:28.0.0' implementation 'com.android.support:multidex:1.0.3' implementation 'com.google.android.gms:play-services-ads:17.2.1' implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.jakewharton:butterknife:10.1.0' annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' implementation 'com.github.bumptech.glide:glide:4.8.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.afollestad.material-dialogs:core:0.9.6.0' implementation 'com.github.siyamed:android-shape-imageview:0.9.+@aar' implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.github.chrisbanes:PhotoView:2.1.3' implementation 'com.google.android:flexbox:1.0.0' implementation 'com.android.support:mediarouter-v7:28.0.0' implementation 'com.android.support:preference-v7:28.0.0' implementation 'com.android.support:exifinterface:28.0.0' implementation 'com.google.android.gms:play-services-maps:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.google.android.gms:play-services-location:16.0.0' implementation 'com.google.android.gms:play-services-places:16.0.0' implementation 'com.google.android.gms:play-services-gcm:16.0.0' implementation 'com.google.android.gms:play-services-analytics:16.0.7' implementation 'com.google.android.gms:play-services-ads-identifier:16.0.0' implementation 'com.google.firebase:firebase-core:16.0.8' implementation 'com.google.firebase:firebase-ads:17.2.1' implementation 'com.firebaseui:firebase-ui-auth:4.1.0' implementation 'com.firebaseui:firebase-ui-database:4.1.0' implementation 'com.firebaseui:firebase-ui-storage:4.1.0' implementation 'com.firebaseui:firebase-ui-firestore:4.1.0' implementation 'com.firebaseui:firebase-ui:4.1.0' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' implementation 'com.google.android.gms:play-services-vision:17.0.2' } ``` 然后,再检查你的 AndroidManifest.xml 文件是否已经声明了相机权限: ``` <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.CAMERA" /> ``` 如果还是找不到 `android.hardware:camera:2.0`,可以尝试在 SDK Manager 中更新你的 Android SDK。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值