美文网首页androidAndroid开发经验谈Android开发
Android图片加载框架Picasso源码分析(基于Picas

Android图片加载框架Picasso源码分析(基于Picas

作者: zhuhean | 来源:发表于2018-05-15 23:34 被阅读102次

    Picasso 是 Android 开发中最受欢迎的图片请求加载框架之一 ,它诞生于 2013 年,距今已有五年的生命。在这五年间 Picasso 发布过 21 个版本更新,而最近的一次更新为今年的 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));
      // 添加 NetworkRequestHandler,用于处理 http 或 https 图片 url
      allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
      // 调用 Collections 的静态方法 unmodifiableList 
      // 返回一个不能进行修改操作的 List 实例,防止 requestHandlers 被修改
      requestHandlers = Collections.unmodifiableList(allRequestHandlers);
      this.stats = stats;
      this.targetToAction = new WeakHashMap<>();
      this.targetToDeferredRequestCreator = new WeakHashMap<>();
      this.indicatorsEnabled = indicatorsEnabled;
      this.loggingEnabled = loggingEnabled;
      this.referenceQueue = new ReferenceQueue<>();
      this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
      this.cleanupThread.start();
    }
    

    到这里 Picasso 实例就创建完毕了。

    2.3 Dispatcher 实例的创建

    Picasso.Builder 类的 build 方法在创建 Picasso 实例前先创建了 Dispatcher 类的实例,Dispatcher 类对后面分发处理图片事件至关重要,这里先看一下它的构造函数:

    final DispatcherThread dispatcherThread;
    final Context context;
    final ExecutorService service;
    final Downloader downloader;
    final Map<String, BitmapHunter> hunterMap;
    final Map<Object, Action> failedActions;
    final Map<Object, Action> pausedActions;
    final Set<Object> pausedTags;
    final Handler handler;
    final Handler mainThreadHandler;
    final Cache cache;
    final Stats stats;
    final List<BitmapHunter> batch;
    final NetworkBroadcastReceiver receiver;
    final boolean scansNetworkChanges;
    
    boolean airplaneMode;
    
    Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
        Downloader downloader, Cache cache, Stats stats) {
      // 创建静态内部类 DispatcherThread 的实例并启动
      this.dispatcherThread = new DispatcherThread();
      this.dispatcherThread.start();
      this.context = context;
      this.service = service;
      this.hunterMap = new LinkedHashMap<>();
      this.failedActions = new WeakHashMap<>();
      this.pausedActions = new WeakHashMap<>();
      this.pausedTags = new LinkedHashSet<>();
      // 创建静态内部类 DispatcherHandler 的实例
      this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
      this.downloader = downloader;
      // 保存前面 Picasso 类传进来的主线程 HANDLER
      this.mainThreadHandler = mainThreadHandler;
      this.cache = cache;
      this.stats = stats;
      this.batch = new ArrayList<>(4);
    }
    

    该构造函数首先创建了 DispatcherThread 实例,而后面 DispatcherHandler 实例的创建用到了 DispatcherThread 中的 Looper。

    DispatcherThread 和 DispatcherHandler 都是 Dispatcher 中的静态内部类:

    static class DispatcherThread extends HandlerThread {
      DispatcherThread() {
        super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
      }
    }
    
    private static class DispatcherHandler extends Handler {
      private final Dispatcher dispatcher;
    
      DispatcherHandler(Looper looper, Dispatcher dispatcher) {
        super(looper);
        this.dispatcher = dispatcher;
      }
    
      @Override public void handleMessage(final Message msg) {
        switch (msg.what) {
          case REQUEST_SUBMIT: {
            Action action = (Action) msg.obj;
            dispatcher.performSubmit(action);
            break;
          }
          case HUNTER_COMPLETE: {
            BitmapHunter hunter = (BitmapHunter) msg.obj;
            dispatcher.performComplete(hunter);
            break;
          }
          default:
            Picasso.HANDLER.post(new Runnable() {
              @Override public void run() {
                throw new AssertionError("Unknown handler message received: " + msg.what);
              }
            });
        }
      }
    }
    

    可以看到,DispatcherThread 继承自 HandlerThread,而 DispatcherHandler 实例是通过 DispatcherThread 的 Looper 创建的,因此 DispatcherHandler 发送的消息将切换到工作线程 (即 DispatcherThread) 中处理,即 DispatcherHandler 的 handleMessage 方法会在工作线程中执行。

    3. Picasso 类中的 load 方法

    获取到 Picasso 实例后,紧接着调用 Picasso 类的 load 方法,该方法主要作用就是创建并返回一个 RequestCreator 实例。

    RequestCreator 类的主要作用就是创建 Request 对象,并提供了一系列的 into 方法来开始图片请求。

    先来看一下 Picasso 类中的 load 方法:

    public RequestCreator load(@Nullable String path) {
      // 如果传进来的 path 为 null,创建并返回一个
      // Uri 为 null 的 RequestCreator 对象。
      if (path == null) {
        return new RequestCreator(this, null, 0);
      }
      // 如果 path 为空字符串,抛出异常。
      if (path.trim().length() == 0) {
        throw new IllegalArgumentException("Path must not be empty.");
      }
      return load(Uri.parse(path));
    }
    

    最后 path 不为 null 也不为空字符串,则调用 Uri.parse(path) 方法对 path 进行解析并返回 一个 Uri 对象传给 load(@Nullable Uri uri) 方法。

    public RequestCreator load(@Nullable Uri uri) {
      return new RequestCreator(this, uri, 0);
    }
    

    该方法创建并返回一个 RequestCreator 实例,看一下 RequestCreator 类的构造函数:

    private final Picasso picasso;
    private final Request.Builder data;
    
    RequestCreator(Picasso picasso, Uri uri, int resourceId) {
      this.picasso = picasso;
      this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
    }
    

    可以看到 RequestCreator 对象持有了 Picasso 的一份引用,然后创建了 Request.Builder 类的实例 data,这里又用到了 Builder 设计模式。

    Request.Builder 的构造函数为:

    Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
      this.uri = uri;
      this.resourceId = resourceId;
      this.config = bitmapConfig;
    }
    

    到这里 RequestCreator 就创建完成了,接下来就可以调用 RequestCreator 类中的许多方法,如 placeholder 方法,centerCrop 方法等,我们这里直接前往 into 方法。

    4. RequestCreator 类的 into 方法

    经过 load 方法后,紧接着就来到了 into 方法,该方法是整个 API 调用流程的最后一步,可以说是整个调用流程的重头戏,因此篇幅也比较大。

    我们往 into 方法传的是 ImageView 实例,看一下该方法源码:

    public void into(ImageView target) {
      into(target, null);
    }
    
    public void into(ImageView target, Callback callback) {
      // 记录开始处理的时间戳
      long started = System.nanoTime();
      // 检查当前方法是否在主线程进行调用,如果不是抛出异常
      checkMain();
      // ImageView 实例 target 不能为 null,否则抛异常
      if (target == null) {
        throw new IllegalArgumentException("Target must not be null.");
      }
      // data 即前面的 Request.Builder 实例
      // 如果 data 中没有图片(例如传入的 path 为 null)
      // 直接对该 target 取消请求,并设置占位图如果有设置 placeholder
      if (!data.hasImage()) {
        picasso.cancelRequest(target);
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
        return;
      }
      // 创建 Request 实例
      Request request = createRequest(started);
      // 为当前 Request 生成一个 requestKey,用来标记 Request
      String requestKey = createKey(request);
      // 如果当前的 memoryPolicy 允许从缓存中读取图片
      // 从 Cache 中获取 requestKey 对应的 Bitmap,如果该 Bitmap 存在
      // 则取消当前请求,直接为 target 设置该 Bitmap
      if (shouldReadFromMemoryCache(memoryPolicy)) {
        Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
        if (bitmap != null) {
          picasso.cancelRequest(target);
          setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
          if (callback != null) {
            callback.onSuccess();
          }
          return;
        }
      }
    
      // 前面缓存中没有查找到图片,从这里开始请求
      // 先设置 placeholder 如果有配置的话 
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
    
      // 创建一个 ImageViewAction 的实例
      Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
              errorDrawable, requestKey, tag, callback, noFade);
      // 向 picasso 提交该 Action 实例
      picasso.enqueueAndSubmit(action);
    }
    

    这里省略了部分与 Picasso.get().load(uri).into(imageView) 调用不相关的代码,代码中的注释只是大致逻辑流程,接下来按 into 方法的代码逻辑对一些细节进行分析。

    4.1 设置占位图 (placeholder) 的实现

    从 into 方法中可以看到,有两处需要设置占位图,设置占位图的逻辑是:

    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }
    

    看一下 getPlaceholderDrawable 方法:

    private Drawable getPlaceholderDrawable() {
      if (placeholderResId != 0) {
        if (Build.VERSION.SDK_INT >= 21) {
          return picasso.context.getDrawable(placeholderResId);
        } else if (Build.VERSION.SDK_INT >= 16) {
          return picasso.context.getResources().getDrawable(placeholderResId);
        } else {
          TypedValue value = new TypedValue();
          picasso.context.getResources().getValue(placeholderResId, value, true);
          return picasso.context.getResources().getDrawable(value.resourceId);
        }
      } else {
        return placeholderDrawable;
      }
    }
    

    该方法根据当前运行的 Android SDK 版本进行不同的方法调用来通过 placeholderResId 获取到一个 Drawable 实例。

    获取到占位图 Drawable 后接下来就可以进行设置了,setPlaceholder 方法为 PicassoDrawable 类中的静态方法,调用该方法后 ImageView 就可以显示占位图了。

    static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {
      target.setImageDrawable(placeholderDrawable);
      if (target.getDrawable() instanceof Animatable) {
        ((Animatable) target.getDrawable()).start();
      }
    }
    

    4.2 Request 实例的创建

    Request 对象是通过 createRequest 方法创建的:

    private static final AtomicInteger nextId = new AtomicInteger();
    
    private Request createRequest(long started) {
      // 为 Request 实例分配下一个 id
      int id = nextId.getAndIncrement();
      // 创建 Request 实例
      Request request = data.build();
      request.id = id;
      request.started = started;
      return request;
    }
    

    Request 实例通过调用 Request.Builder 类的 build 方法创建:

    public Request build() {
      // 先验证当前的配置参数是否合法
      // centerInside 和 centerCrop 方法不能同时用
      if (centerInside && centerCrop) {
        throw new IllegalStateException("Center crop and center inside can not be used together.");
      }
      // centerCrop 方法需要与 resize 方法同用
      if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {
        throw new IllegalStateException(
            "Center crop requires calling resize with positive width and height.");
      }
      // centerInside 方法需要与 resize 方法同用
      if (centerInside && (targetWidth == 0 && targetHeight == 0)) {
        throw new IllegalStateException(
            "Center inside requires calling resize with positive width and height.");
      }
      // 设置 priority
      if (priority == null) {
        priority = Priority.NORMAL;
      }
      // 创建 Request 实例
      return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
          centerCrop, centerInside, centerCropGravity, onlyScaleDown, rotationDegrees,
          rotationPivotX, rotationPivotY, hasRotationPivot, purgeable, config, priority);
    }
    

    该方法在最后一步创建了 Request 实例,看一下 Request 类的构造函数:

    public final Uri uri;
    public final int resourceId;
    public final String stableKey;
    public final List<Transformation> transformations;
    public final int targetWidth;
    public final int targetHeight;
    public final boolean centerCrop;
    public final int centerCropGravity;
    public final boolean centerInside;
    public final boolean onlyScaleDown;
    public final float rotationDegrees;
    public final float rotationPivotX;
    public final float rotationPivotY;
    public final boolean hasRotationPivot;
    public final boolean purgeable;
    public final Bitmap.Config config;
    public final Priority priority;
    
    private Request(Uri uri, int resourceId, String stableKey, List<Transformation> transformations,
        int targetWidth, int targetHeight, boolean centerCrop, boolean centerInside,
        int centerCropGravity, boolean onlyScaleDown, float rotationDegrees,
        float rotationPivotX, float rotationPivotY, boolean hasRotationPivot,
        boolean purgeable, Bitmap.Config config, Priority priority) {
      // 图片的 Uri,与 resourceId 不能共存
      this.uri = uri;
      // 图片的 resourceId,与 Uri 不能共存
      this.resourceId = resourceId;
      this.stableKey = stableKey;
      // 用来对 Bitmap 进行转换的一系列 Transformation
      if (transformations == null) {
        this.transformations = null;
      } else {
        this.transformations = unmodifiableList(transformations);
      }
      // resize 方法设置的图片宽度和高度
      this.targetWidth = targetWidth;
      this.targetHeight = targetHeight;
      // 图片 scaleType 是否为 centerCrop,与 centerInside 不共存
      this.centerCrop = centerCrop;
      // 图片 scaleType 是否为 centerInside,与 centerCrop 不共存
      this.centerInside = centerInside;
      // 如果设置了 centerCrop,centerCropGravity 用来设置中心的偏移量
      this.centerCropGravity = centerCropGravity;
      this.onlyScaleDown = onlyScaleDown;
      // 图片旋转的度数
      this.rotationDegrees = rotationDegrees;
      this.rotationPivotX = rotationPivotX;
      this.rotationPivotY = rotationPivotY;
      this.hasRotationPivot = hasRotationPivot;
      this.purgeable = purgeable;
      this.config = config;
      // 当前请求的优先级
      this.priority = priority;
    }
    

    Request 实例创建完毕。

    4.3 requestKey 的创建

    requestKey 用来标识一个 Request,requestKey 通过调用 createKey 方法实现:

    static final StringBuilder MAIN_THREAD_KEY_BUILDER = new StringBuilder();
    
    static String createKey(Request data) {
      String result = createKey(data, MAIN_THREAD_KEY_BUILDER);
      MAIN_THREAD_KEY_BUILDER.setLength(0);
      return result;
    }
    
    static String createKey(Request data, StringBuilder builder) {
      if (data.stableKey != null) {
        builder.ensureCapacity(data.stableKey.length() + KEY_PADDING);
        builder.append(data.stableKey);
      } else if (data.uri != null) {
        String path = data.uri.toString();
        builder.ensureCapacity(path.length() + KEY_PADDING);
        builder.append(path);
      } else {
        builder.ensureCapacity(KEY_PADDING);
        builder.append(data.resourceId);
      }
      builder.append(KEY_SEPARATOR);
    
      if (data.rotationDegrees != 0) {
        builder.append("rotation:").append(data.rotationDegrees);
        if (data.hasRotationPivot) {
          builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY);
        }
        builder.append(KEY_SEPARATOR);
      }
      if (data.hasSize()) {
        builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight);
        builder.append(KEY_SEPARATOR);
      }
      if (data.centerCrop) {
        builder.append("centerCrop:").append(data.centerCropGravity).append(KEY_SEPARATOR);
      } else if (data.centerInside) {
        builder.append("centerInside").append(KEY_SEPARATOR);
      }
    
      if (data.transformations != null) {
        for (int i = 0, count = data.transformations.size(); i < count; i++) {
          builder.append(data.transformations.get(i).key());
          builder.append(KEY_SEPARATOR);
        }
      }
    
      return builder.toString();
    }
    

    该方法根据当前 Request 配置的参数来生成对应的 requestKey。

    4.4 MemoryPolicy 的实现

    into 方法通过调用 shouldReadFromMemoryCache 方法来判断是否应该从 Cache 中读取当前 requestKey 对应的 Bitmap。

    shouldReadFromMemoryCache 方法是枚举类型 MemoryPolicy 中的一个静态方法,MemoryPolicy 源码如下:

    public enum MemoryPolicy {
    
      NO_CACHE(1 << 0),
      NO_STORE(1 << 1);
    
      static boolean shouldReadFromMemoryCache(int memoryPolicy) {
        return (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0;
      }
    
      static boolean shouldWriteToMemoryCache(int memoryPolicy) {
        return (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0;
      }
    
      final int index;
    
      MemoryPolicy(int index) {
        this.index = index;
      }
        
    }
    

    可以看到 MemoryPolicy 用到了一些位操作。

    MemoryPolicy 共两种枚举类型,NO_CACHE 和 NO_STORE,NO_CACHE 的 index 为 1 (二进制为 1),NO_STORE 的 index 为 2 (二进制为 10)。

    shouldReadFromMemoryCache 方法返回 true 如果 (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0 ,即 memoryPolicy 为 0 。返回 true 表示当前 memoryPolicy 允许从 Cache 中读取图片。

    shouldWriteToMemoryCache 方法在满足 (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0 的条件下返回 true,即 memoryPolicy 为 0。返回 true 表示当前 memoryPolicy 允许向 Cache 中写入图片。

    而在没有配置 memoryPolicy 的情况下,memoryPolicy 默认为 0,因此这两个方法这里都会返回 true。

    4.5 Action 实例的创建

    Picasso 将一次图片获取活动封装成一个 Action 实例。

    Action 为抽象类,包含两个必须实现的抽象方法,complete 方法和 error 方法,分别表示该次图片获取活动完成或出错。

    abstract void complete(Bitmap result, Picasso.LoadedFrom from);
    abstract void error(Exception e);
    

    Picasso 提供了不同的 Action 子类来对应不同的图片获取活动。

    我们这里用到的是 ImageViewAction,ImageViewAction 用于将获取到的 Bitmap 加载到 ImageView 中:

    class ImageViewAction extends Action<ImageView> {
    
      Callback callback;
    
      ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
          int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
          Callback callback, boolean noFade) {
        super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
            tag, noFade);
        this.callback = callback;
      }
    
      @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
        // 获取要加载图片进去的 ImageView
        ImageView target = this.target.get();
        if (target == null) return;
        Context context = picasso.context;
        boolean indicatorsEnabled = picasso.indicatorsEnabled;
        // 为 target 设置 Bitmap
        PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
        // 回调 callback
        if (callback != null) callback.onSuccess();
      }
    
      @Override public void error(Exception e) {
        ImageView target = this.target.get();
        if (target == null) return;
        // 获取占位图 Drawable 实例,如果该 Drawable 实现了 Animatable 接口
        // 这时候就应该停止该 Animatable
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof Animatable) ((Animatable) placeholder).stop();
        // 设置错误情况下的图片
        if (errorResId != 0) {
          target.setImageResource(errorResId);
        } else if (errorDrawable != null) {
          target.setImageDrawable(errorDrawable);
        }
        if (callback != null) callback.onError(e);
      }
    
      @Override void cancel() {
        super.cancel();
        if (callback != null) callback = null;
      }
    }
    

    可以看到 ImageViewAction 类继承自 Action,泛型参数为 ImageView 作为该 Action 的 target。

    ImageViewAction 实现了 complete 方法和 error 方法并重写了 cancel 方法。

    4.6 Action 实例的提交

    Action 实例创建完毕后,就可以调用 Picasso 类中的 enqueueAndSubmit 方法提交该 Action 实例了,然后从这里开始一次图片获取活动,由于这部分代码过于庞大,从 enqueueAndSubmit 方法开始放在下一节分析。

    5. Picasso 处理 Action 的实现

    Picasso 处理 Action 从 Picasso 类的 enqueueAndSubmit 方法开始:

    void enqueueAndSubmit(Action action) {
      // 省略部分代码...
      // 调用 submit 方法提交该 Action
      submit(action);
    }
    
    void submit(Action action) {
      dispatcher.dispatchSubmit(action);
    }
    

    submit 方法又调用了 Dispatcher 类的 dispatchSubmit 方法,Picasso 类这时候就将此 Action 交接给 Dispatcher 类进行处理。

    5.1 Dispatcher 类提交 Action 的实现

    Picasso 通过调用 Dispatcher 类的 dispatchSubmit 方法开始提交该 Action 实例:

    void dispatchSubmit(Action action) {
      handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
    }
    

    该方法使用 handler 发送了一条 REQUEST_SUBMIT 信息,我们在 2.3 小节中可以看到,该 handler 即 DispatcherHandler 实例,发送该消息后会在工作线程回调到 DispatcherHandler 中的 handleMessage 方法,然后从该方法接着会调用 Dispatcher 类的 performSubmit 方法:

    void performSubmit(Action action) {
      performSubmit(action, true);
    }
    
    void performSubmit(Action action, boolean dismissFailed) {
      // 创建 BitmapHunter 实例
      BitmapHunter hunter = forRequest(action.getPicasso(), this, cache, stats, action);
      // 使用配置好的 PicassoExecutorService 提交该 BitmapHunter 实例
      hunter.future = service.submit(hunter);
    }
    

    上面的代码做了一些简化,删除了与当前逻辑无关的代码。

    该方法通过调用 forRequest 方法来创建 BitmapHunter 实例,而 forRequest 方法是 BitmapHunter 类中的静态方法。

    从上面的代码可以看到,接下来的工作就交给 BitmapHunter 来完成了。

    5.2 BitmapHunter 类的实现

    BitmapHunter 类的主要职责是结合 Action 和 RequestHandler 来获取 Bitmap 实例。

    BitmapHunter 提供了静态方法 forRequest 来创建实例:

    static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
        Action action) {
      // 获取 Action 中的 Request 对象
      Request request = action.getRequest();
      // 获取 Picasso 配置的全部 RequestHandler
      List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
      // 从下标 0 开始迭代全部 RequestHandler,如果该 RequestHandler 能
      // 处理该 Request,则用该 RequestHandler 创建 BitmapHunter 实例并返回。
      for (int i = 0, count = requestHandlers.size(); i < count; i++) {
        RequestHandler requestHandler = requestHandlers.get(i);
        if (requestHandler.canHandleRequest(request)) {
          return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
        }
      }
      // 没有 RequestHandler 能处理该 Request,传入 ERRORING_HANDLER
      return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
    }
    

    这里的 requestHandlers 部分用到了责任链模式 (Chains of Responsibility)。

    接着看下 BitmapHunter 的构造函数:

    final int sequence;
    final Picasso picasso;
    final Dispatcher dispatcher;
    final Cache cache;
    final Stats stats;
    final String key;
    final Request data;
    final int memoryPolicy;
    int networkPolicy;
    final RequestHandler requestHandler;
    
    Action action;
    List<Action> actions;
    Bitmap result;
    Future<?> future;
    Picasso.LoadedFrom loadedFrom;
    Exception exception;
    int exifOrientation;
    int retryCount;
    Priority priority;
    
    BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
        RequestHandler requestHandler) {
      this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
      this.picasso = picasso;
      this.dispatcher = dispatcher;
      this.cache = cache;
      this.stats = stats;
      this.action = action;
      this.key = action.getKey();
      this.data = action.getRequest();
      this.priority = action.getPriority();
      this.memoryPolicy = action.getMemoryPolicy();
      this.networkPolicy = action.getNetworkPolicy();
      this.requestHandler = requestHandler;
      this.retryCount = requestHandler.getRetryCount();
    }
    

    BitmapHunter 类实现了 Runnable 接口,因此当前面调用 service.submit(hunter) ,BitmapHunter 类中的 run 方法会在线程池中运行:

    @Override public void run() {
      try {
        result = hunt();
        if (result == null) {
          dispatcher.dispatchFailed(this);
        } else {
          dispatcher.dispatchComplete(this);
        }
      } catch (Exception e) {
        // 处理一堆异常...
      }
    }
    

    run 方法主要通过调用 hunt 方法获取返回的 Bitmap 并赋值给 result,如果 result 不为 null,则调用 Dispatcher 类的 dispatchComplete 方法,否则调用 dispatchFailed 方法。

    因此获取 Bitmap 的具体逻辑就在 hunt 方法中完成:

    Bitmap hunt() throws IOException {
      Bitmap bitmap = null;
      // 先从缓存中查找 key 对应的 Bitmap,该 key 即之前创建的 requestKey
      if (shouldReadFromMemoryCache(memoryPolicy)) {
        bitmap = cache.get(key);
        if (bitmap != null) {
          stats.dispatchCacheHit();
          loadedFrom = MEMORY;
          return bitmap;
        }
      }
      // 调用 RequestHandler 的 load 方法获取 RequestHandler.Result 实例
      // Picasso 将 RequestHandler 加载的结果封装成一个 Result 对象
      // 我们这里调用的是 NetworkRequestHandler 类中的 load 方法
      RequestHandler.Result result = requestHandler.load(data, networkPolicy);
      if (result != null) {
        loadedFrom = result.getLoadedFrom();
        exifOrientation = result.getExifOrientation();
        bitmap = result.getBitmap();
        // result 中的 bitmap 为 null,将 source 中的字节流编码成 Bitmap
        if (bitmap == null) {
          Source source = result.getSource();
          try {
            bitmap = decodeStream(source, data);
          } finally {
            try {
              source.close();
            } catch (IOException ignored) {
            }
          }
        }
      }
      return bitmap;
    }
    

    hunt 方法结束,返回获取到的 Bitmap 实例,回到 BitmapHunter 的 run 方法,这时候如果返回的 Bitmap 实例不为null,就调用 Dispatcher 类中的 dispatchComplete 方法,这样剩下的工作又交接给了 Dispatcher。

    5.3 Dispatcher 类请求完成的实现

    BitmapHunter 获取到不为 null 的 Bitmap 实例后,调用 Dispatcher 类中的 dispatchComplete 方法分发其完成事件:

    void dispatchComplete(BitmapHunter hunter) {
      handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
    }
    

    handler 发送该消息后,接着又在工作线程中回调到了 Dispatcher 类的 performComplete 方法:

    void performComplete(BitmapHunter hunter) {
      if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
        cache.set(hunter.getKey(), hunter.getResult());
      }
      batch(hunter);
    }
    

    这时候 Picasso 将 Bitmap 存入到了缓存中,然后调用 batch 方法:

    private void batch(BitmapHunter hunter) {
      if (hunter.isCancelled()) {
        return;
      }
      if (hunter.result != null) {
        hunter.result.prepareToDraw();
      }
      batch.add(hunter);
      if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
        handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
      }
    }
    

    handler 最后发送了 HUNTER_DELAY_NEXT_BATCH 消息,发送该消息后又回调到了 Dispatcher 类的 performBatchComplete 方法:

    void performBatchComplete() {
      batch.clear();  mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    }
    

    这时候发送消息的是 mainThreadHandler,切换到主线程来了,发送的消息为 HUNTER_BATCH_COMPLETE,该消息回调到了 Picasso 类中的 HANDLER 实例中:

    static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
      @Override public void handleMessage(Message msg) {
        switch (msg.what) {
          case HUNTER_BATCH_COMPLETE: {
            List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
            for (int i = 0, n = batch.size(); i < n; i++) {
              BitmapHunter hunter = batch.get(i);
              hunter.picasso.complete(hunter);
            }
            break;
          }
          default:
            throw new AssertionError("Unknown handler message received: " + msg.what);
        }
      }
    };
    

    对每个 BitmapHunter 实例调用 Picasso 类中的 complete 方法:

    void complete(BitmapHunter hunter) {
      Action single = hunter.getAction();
      if (single == null) return;
    
      Uri uri = hunter.getData().uri;
      Exception exception = hunter.getException();
        
      Bitmap result = hunter.getResult();
      LoadedFrom from = hunter.getLoadedFrom();
      
      deliverAction(result, from, single, exception);
    
      if (listener != null && exception != null) {
        listener.onImageLoadFailed(this, uri, exception);
      }
    }
    
    private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
      if (action.isCancelled()) {
        return;
      }
      if (result != null) {
        if (from == null) {
          throw new AssertionError("LoadedFrom cannot be null.");
        }
        action.complete(result, from);
        }
      } else {
        action.error(e);
      }
    }
    

    我们之前提交的是 ImageViewAction 实例,因此这时候会回调到 ImageViewAction 类的 complete 方法:

    @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
      ImageView target = this.target.get();
      if (target == null) return;
    
      Context context = picasso.context;
      boolean indicatorsEnabled = picasso.indicatorsEnabled;
      PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    
      if (callback != null) {
        callback.onSuccess();
      }
    }
    

    最后 setBitmap:

    static void setBitmap(ImageView target, Context context, Bitmap bitmap,
        Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
      Drawable placeholder = target.getDrawable();
      if (placeholder instanceof Animatable) {
        ((Animatable) placeholder).stop();
      }
      PicassoDrawable drawable =
          new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
      target.setImageDrawable(drawable);
    }
    

    结束该方法后,这时候图片终于成功在界面显示了,整个调用流程到此也终于结束。

    6. 参考

    相关文章

      网友评论

      • javalong:写的很好啊,整个流程都过了一遍

      本文标题:Android图片加载框架Picasso源码分析(基于Picas

      本文链接:https://www.haomeiwen.com/subject/hicudftx.html