Picasso图片加载源码分析

一.我们先来看看哪些重要的实现类:
Picasso: 图片加载、转换、缓存的管理类。单列模式 ,通过with方法获取实例,也是加载图片的入口。
RequestCreator: Request构建类,Builder 模式,采用链式设置该Request的属性(如占位图、缓存策略、裁剪规则、显示大小、优先级等等)。最后调用build()方法生成一个请求(Request)。
DeferredRequestCreator:RequestCreator的包装类,当创建请求的时候还不能获取ImageView的宽和高的时候,则创建一个DeferredRequestCreator,DeferredRequestCreator里对 target 设置监听,直到可以获取到宽和高的时候重新执行请求创建。
 Action: 请求包装类,存储了该请求和RequestCreator设置的这些属性,最终提交给线程执行下载。
Dispatcher:分发器,分发执行各种请求、分发结果等等。
PicassoExecutorService:Picasso使用的线程池,默认池大小为3。
LruCache:一个使用最近最少使用策略的内存缓存。
BitmapHunter:这是Picasso的一个核心的类,开启线程执行下载,获取结果后解码成Bitmap,然后做一些转换操作如图片旋转、裁剪等,如果请求设置了转换器Transformation,也会在BitmapHunter里执行这些转换操作。
NetworkRequestHandler:网络请求处理器,如果图片需要从网络下载,则用这个处理器处理。
FileRequestHandler:文件请求处理器,如果请求的是一张存在文件中的图片,则用这个处理器处理。
AssetRequestHandler: Asset 资源图片处理器,如果是加载asset目录下的图片,则用这个处理器处理。
ResourceRequestHandler:Resource资源图片处理器,如果是加载res下的图片,则用这个处理器处理。
ContentStreamRequestHandler: ContentProvider 处理器,如果是ContentProvider提供的图片,则用这个处理器处理
MediaStoreRequestHandler: MediaStore 请求处理器,如果图片是存在MediaStore上的则用这个处理器处理。
ContactsPhotoRequestHandler:ContactsPhoto 请求处理器,如果加载com.android.contacts/ 下的tu图片用这个处理器处理
Response: 返回的结果信息,Stream流或者Bitmap。
Request: 请求实体类,存储了应用在图片上的信息。
Target:图片加载的监听器接口,有3个回调方法,onPrepareLoad 在请求提交前回调,onBitmapLoaded 请求成功回调,并返回Bitmap,onBitmapFailed请求失败回调。
PicassoDrawable:继承BitmapDrawable,实现了过渡动画和图片来源的标识(就是图片来源的指示器,要调用 setIndicatorsEnabled(true)方法才生效),请求成功后都会包装成BitmapDrawable显示到ImageView 上。
OkHttpDownloader:用OkHttp实现的图片下载器,默认就是用的这个下载器。
UrlConnectionDownloader:使用HttpURLConnection 实现的下载器。
MemoryPolicy: 内存缓存策略,一个枚举类型。
NetworkPolicy: 磁盘缓存策略,一个枚举类型。
Stats: 这个类相当于日志记录,会记录如:内存缓存的命中次数,丢失次数,下载次数,转换次数等等,我们可以通过StatsSnapshot类将日志打印出来,看一下整个项目的图片加载情况。
StatsSnapshot :状态快照,和上面的Stats对应,打印Stats纪录的信息。
二.基本使用
Picasso.with(this).load(URL)
                .placeholder(R.drawable.icon_bg)
                .error(R.drawable.icon_iamge)
                .into(image);
三.源码分析
1.首先要获取一个Picasso对象,采用的单例模式
//单例模式获取Picasso 对象
public static Picasso with(Context context) {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }

// 真正new 的地方在build()方法里
 public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        //配置默认的下载器,首先通过反射获取OkhttpClient,如果获取到了,就使用OkHttpDwownloader作为默认下载器
        //如果获取不到就使用UrlConnectionDownloader作为默认下载器
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {
       // 配置内存缓存,大小为手机内存的15%
        cache = new LruCache(context);
      }
      if (service == null) {
       // 配置Picaso 线程池,核心池大小为3
        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.通过load方法生成一个RequestCreator,用链式api 来构建一个图片下载请求,RequestCreator提供了很多的API 来构建请求,如展位图、大小、转换器、裁剪等等,这些API其实是为对应的属性赋值,最终会在into方法中构建请求。
  //load有几个重载方法,参数为string和File 的重载最重都会包装成一个Uri 调用这个方法
   public RequestCreator load(Uri uri) {
      return new RequestCreator(this, uri, 0);
    }
  // 如果是加载资源id 的图片会调用这个方法
   public RequestCreator load(int resourceId) {
      if (resourceId == 0) {
        throw new IllegalArgumentException("Resource ID must not be zero.");
      }
      return new RequestCreator(this, null, resourceId);
    }

    // 配置占位图,在加载图片的时候显示
    public RequestCreator placeholder(int placeholderResId) {
        if (!setPlaceholder) {
          throw new IllegalStateException("Already explicitly declared as no placeholder.");
        }
        if (placeholderResId == 0) {
          throw new IllegalArgumentException("Placeholder image resource invalid.");
        }
        if (placeholderDrawable != null) {
          throw new IllegalStateException("Placeholder image already set.");
        }
        this.placeholderResId = placeholderResId;
        return this;
      }

    // 配置真正显示的大小
     public RequestCreator resize(int targetWidth, int targetHeight) {
        data.resize(targetWidth, targetHeight);
        return this;
      }
  3.into 添加显示的View,并且提交下载请求
  into方法里面干了3件事情:
  1.判断是否设置了fit 属性,如果设置了,再看是否能够获取ImageView 的宽高,如果获取不到,生成一个DeferredRequestCreator(延迟的请求管理器),然后直接return,在DeferredRequestCreator中当监听到可以获取ImageView 的宽高的时候,再执行into方法。
  2.判断是否从内存缓存获取图片,如果没有设置NO_CACHE,则从内存获取,命中直接回调CallBack 并且显示图片。
  3.如果缓存未命中,则生成一个Action,并提交Action。
  public void into(ImageView target, Callback callback) {
      long started = System.nanoTime();
     // 检查是否在主线程
      checkMain();

      if (target == null) {
        throw new IllegalArgumentException("Target must not be null.");
      }
     //如果没有url或者resourceId 则取消请求
      if (!data.hasImage()) {
        picasso.cancelRequest(target);
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
        return;
      }
      //判断是否设置了fit属性
      if (deferred) {
        if (data.hasSize()) {
          throw new IllegalStateException("Fit cannot be used with resize.");
        }
        int width = target.getWidth();
        int height = target.getHeight();
        if (width == 0 || height == 0) {
          if (setPlaceholder) {
            setPlaceholder(target, getPlaceholderDrawable());
          }
         //如果获取不到宽高,生成一个DeferredRequestCreator(延迟的请求管理器),然后直接return,
        //在DeferredRequestCreator中当监听到可以获取ImageView 的宽高的时候,再执行into方法。
          picasso.defer(target, new DeferredRequestCreator(this, target, callback));
          return;
        }
        data.resize(width, height);
      }

      Request request = createRequest(started);
      String requestKey = createKey(request);
      //是否从内存缓存中获取
      if (shouldReadFromMemoryCache(memoryPolicy)) {
        Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
        if (bitmap != null) {
         //缓存命中,取消请求,并显示图片
          picasso.cancelRequest(target);
          setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
          if (picasso.loggingEnabled) {
            log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
          }
          if (callback != null) {
            callback.onSuccess();
          }
          return;
        }
      }

      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
     //内存缓存未命中或者设置了不从内存缓存获取,则生成一个Action ,提交执行。
      Action action =
          new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
              errorDrawable, requestKey, tag, callback, noFade);

      picasso.enqueueAndSubmit(action);// 提交请求
    }
 4.提交、分发、执行请求
 会经过下面这一系列的操作,最重将Action 交给BitmapHunter 执行。
 enqueueAndSubmit -> submit -> dispatchSubmit -> performSubmit:
 //将action 保存到了一个Map 中,目标View作为key
  void enqueueAndSubmit(Action action) {
     Object target = action.getTarget();
     if (target != null && targetToAction.get(target) != action) {
       // This will also check we are on the main thread.
       cancelExistingRequest(target);
       targetToAction.put(target, action);
     }
     submit(action);
   }

 // 交给分发器分发提交请求
 void submit(Action action) {
     dispatcher.dispatchSubmit(action);
   }

 //执行请求提交
 //1, 先查看保存暂停tag表里面没有包含Action的tag,如果包含,则将Action 存到暂停Action表里
 //2,从BitmapHunter表里查找有没有对应action的hunter,如果有直接attach
 //3, 为这个请求生成一个BitmapHunter,提交给线程池执行
  void performSubmit(Action action, boolean dismissFailed) {
     // 先查看保存暂停tag表里面没有包含Action的tag,如果包含,则将Action 存到暂停Action表里
     if (pausedTags.contains(action.getTag())) {
       pausedActions.put(action.getTarget(), action);
       if (action.getPicasso().loggingEnabled) {
         log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
             "because tag '" + action.getTag() + "' is paused");
       }
       return;
     }

     BitmapHunter hunter = hunterMap.get(action.getKey());
     if (hunter != null) {
       hunter.attach(action);
       return;
     }
    // 如果线程池北shutDown,直接return
     if (service.isShutdown()) {
       if (action.getPicasso().loggingEnabled) {
         log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
       }
       return;
     }
     // 为请求生成一个BitmapHunter
     hunter = forRequest(action.getPicasso(), this, cache, stats, action);
     hunter.future = service.submit(hunter);//提交执行
     hunterMap.put(action.getKey(), hunter);
     if (dismissFailed) {
       failedActions.remove(action.getTarget());
     }

     if (action.getPicasso().loggingEnabled) {
       log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
     }
   }
   5.指定对应的处理器(RequestHandler)
   在上面执行的请求的performSubmit 方法里,调用了forRequest 方法为对应的Action 生成一个BitmapHunter,里面有一个重要的步骤,指定请求处理器(在上面一节介绍Picasso有7种请求处理器,看一下对应的代码:
   // 1,首先调用了getRequestHandlers
    List<RequestHandler> getRequestHandlers() {
       return requestHandlers;
     }

   // 2 requestHandlers 列表是在Picasso 构造函数里出实话的
    Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
         RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
         Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {

       ....
      //前面代码省略

      // 添加了7个内置的请求处理器
     // 如果你自己通过Builder添了额外的处理器,也会添加在这个列表里面

       int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
       int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
       List<RequestHandler> allRequestHandlers =
           new ArrayList<RequestHandler>(builtInHandlers + extraCount);

       // ResourceRequestHandler needs to be the first in the list to avoid
       // forcing other RequestHandlers to perform null checks on request.uri
       // to cover the (request.resourceId != 0) case.
       allRequestHandlers.add(new ResourceRequestHandler(context));
       if (extraRequestHandlers != null) {
         allRequestHandlers.addAll(extraRequestHandlers);
       }
       allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
       allRequestHandlers.add(new MediaStoreRequestHandler(context));
       allRequestHandlers.add(new ContentStreamRequestHandler(context));
       allRequestHandlers.add(new AssetRequestHandler(context));
       allRequestHandlers.add(new FileRequestHandler(context));
       allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
       requestHandlers = Collections.unmodifiableList(allRequestHandlers);

     //后面代码省略
       ...

     }

 6.介绍过BitmapHunter,BitmapHunter继承Runnable,其实就是开启一个线程执行最终的下载。看一下源码:
 @Override public void run() {
     try {
       updateThreadName(data);

       if (picasso.loggingEnabled) {
         log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
       }
      //  调用hunt() 方法获取最终结果
       result = hunt();

       if (result == null) {
         dispatcher.dispatchFailed(this);//如果为null,分发失败的消息
       } else {
         dispatcher.dispatchComplete(this);//如果不为null,分发成功的消息
       }
     } catch (Downloader.ResponseException e) {
       if (!e.localCacheOnly || e.responseCode != 504) {
         exception = e;
       }
       dispatcher.dispatchFailed(this);
     } catch (NetworkRequestHandler.ContentLengthException e) {
       exception = e;
       dispatcher.dispatchRetry(this);
     } catch (IOException e) {
       exception = e;
       dispatcher.dispatchRetry(this);
     } catch (OutOfMemoryError e) {
       StringWriter writer = new StringWriter();
       stats.createSnapshot().dump(new PrintWriter(writer));
       exception = new RuntimeException(writer.toString(), e);
       dispatcher.dispatchFailed(this);
     } catch (Exception e) {
       exception = e;
       dispatcher.dispatchFailed(this);
     } finally {
       Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
     }
   }
   当将一个bitmapHunter submit 给一个线程池执行的时候,就会执行run() 方法,run里面调用的是hunt方法来获取结果
   7.Downloader 下载器下载图片
   上面的hunt方法获取结果的时候,最终调用的是配置的处理器的load方法,如下:
   RequestHandler.Result result = requestHandler.load(data, networkPolicy);
   NetworkRequestHandler最终是调用的downloader 的load方法下载图片。内置了2个Downloader,OkhttpDownloader和UrlConnectionDownloader 。我们以UrlConnectionDownloader为例,来看一下load方法:
   @Override public Response load(Uri uri, int networkPolicy) throws IOException {
      // 如果SDK 版本大于等于14,安装磁盘缓存,用的是HttpResponseCache(缓存http或者https的response到文件系统)
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
         installCacheIfNeeded(context);
       }

       HttpURLConnection connection = openConnection(uri);
      //设置使用缓存
       connection.setUseCaches(true);

       if (networkPolicy != 0) {
         String headerValue;
        // 下面一段代码是设置缓存策略
         if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
           headerValue = FORCE_CACHE;
         } else {
           StringBuilder builder = CACHE_HEADER_BUILDER.get();
           builder.setLength(0);

           if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
             builder.append("no-cache");
           }
           if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
             if (builder.length() > 0) {
               builder.append(',');
             }
             builder.append("no-store");
           }

           headerValue = builder.toString();
         }

         connection.setRequestProperty("Cache-Control", headerValue);
       }

       int responseCode = connection.getResponseCode();
       if (responseCode >= 300) {
         connection.disconnect();
         throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
             networkPolicy, responseCode);
       }

       long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
       boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
       // 最后获取InputStream流包装成Response返回
       return new Response(connection.getInputStream(), fromCache, contentLength);
     }

   8.显示在控件上
   在BitmapHunter获取结果后,分发器分发结果,通过Hander处理后,执行performComplete方法:
   void performComplete(BitmapHunter hunter) {
       // 这里将结果缓存到内存
       if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
         cache.set(hunter.getKey(), hunter.getResult());
       }
       hunterMap.remove(hunter.getKey());// 请求完毕,将hunter从表中移除
       batch(hunter);
       if (hunter.getPicasso().loggingEnabled) {
         log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
       }
     }
   
   // 2,然后将BitmapHunter添加到一个批处理列表,通过Hander发送一个批处理消息
   private void batch(BitmapHunter hunter) {
       if (hunter.isCancelled()) {
         return;
       }
       batch.add(hunter);
       if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
         handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
       }
     }
   // 3,最后执行performBatchComplete 方法,通过主线程的Handler送处理完成的消息
   void performBatchComplete() {
       List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
       batch.clear();
       mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
       logBatch(copy);
     }
   
   // 4,最后在Picasso 中handleMessage,显示图片
    static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
       @Override public void handleMessage(Message msg) {
         switch (msg.what) {
           case HUNTER_BATCH_COMPLETE: {
             @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
             //noinspection ForLoopReplaceableByForEach
             for (int i = 0, n = batch.size(); i < n; i++) {
               BitmapHunter hunter = batch.get(i);
               hunter.picasso.complete(hunter);
             }
             break;
           }
         //后面代码省略
         ...
     };
   // 5,最后回调到ImageViewAction 的complete方法显示图片
   @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
       if (result == null) {
         throw new AssertionError(
             String.format("Attempted to complete action with no result!\n%s", this));
       }
   
       ImageView target = this.target.get();
       if (target == null) {
         return;
       }
   
       Context context = picasso.context;
       boolean indicatorsEnabled = picasso.indicatorsEnabled;
      //将结果包装成一个PicassoDrawable 并显示
       PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
   
       if (callback != null) {
         callback.onSuccess(); 回调callback
       }
     }
   
  以上8步就是图片加载整个过程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值