Picasso源码的简单解析(一)

前段时间简单的分析了一下ImageLoader的源码,所以就想看看使用很火的一些其他的图片加载库的实现,跟ImageLoader对比起来有什么优缺点。所以本系列的几篇博文会时不时跟ImageLoader来个简单的对比来说明问题。闲言少叙,开始Picasso分析之旅吧。 
本篇只是简单的对流程进行梳理,因为Picasso自己分析起来篇幅只多不少,所以本篇就简单的说明。具体的某个知识点的细节会另外开篇博客说明之。 
其实既然是缓存,肯定核心也就是那么几点:memory cache,disk cache等等,总体上来说只是不同的库对它们处理的方式不同而已。 
当年老毛同志怎么说的来着:”要从战略上藐视对方,从战术上重视对方“。所以为了减少对阅读源码的恐惧感,让我先从战略上藐视一下picasso吧。刚把这玩意的源码下载来一瞅,第一个感觉就是:我滴个乖乖,居然就一个包!神马各种Action、各种RequestHandler统统特么的一股脑的放在了一个包下面,感觉就像一个洁白的床上堆满了臭袜子、毛巾、衣服和拖鞋一样乱糟糟的。就算你这个作者是技术大神,也不能组织类的时候这么不修边幅啊! 
好了,战略藐视完毕,还是从战术上来仔细的分析吧!

Picasso的总体流程:

总的来说Picasso的流程很简单,当Picasso通过load方法获取图片的时候,需要经过如下步骤才能完成显示图片的流程: 
1)将请求封装为Request对象,然后将Request对象进一步封装为Action(ImageAction)对象。 
2)将Action(ImageAction)对象交给Dispather进行分发 
3)最终将action交给BitmapHunter这个Runnable作为在线程池中线程的工作的单元(具体的是讲action持有的当前Reqeuest对象) 
4)由RequestHandler来处理当前request,调用其load方法将加载完成后的图片交给PicassoDrawable显示图片。 
代码流程如下:Picasso->load->创建request->创建action->Dispatcher分发action->RequestHandler的load方法处理具体的请求->PicassoDrawable显示图片。

上面去掉了许多的枝枝蔓蔓简单的说了一些流程,算是一个总纲吧,下面就围绕着这个总纲来说明Picasso的具体细节。

Picasso对象的初始化

跟ImageLoaderConfiguration一样,Picasso也是利用了Builder模式来组建Picasso,用Builder模式的好处之一就是可以通过Builder来清晰的知道Picasso可都可以提供哪些对外的配置接口供我们使用,同时我们自己在客户端配置这些组件的话,Builder也可以提供默认的组件来使用。就让我们看看Picasso的Builder都提供了什么组件让客户端自由配置:

public static class Builder {
    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;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

先不说这些Builder怎么使用它们,事实上Picasso的with方法来初始化一个Picasso单例对象,事实上就是用的Builder默认的配置来build一个Picasso对象出来:

  static volatile Picasso singleton = null;
public static Picasso with(@NonNull Context context) {
     。。。
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
           //通过Builder默认的配置来创建一个Picasso
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

那么这个默认的Builder在build的时候都做了些神马呢,其实不用多想也应该知道了,就是配置了诸如默认的下载器,请求转换器,默认的memory cache等:

 public Picasso build() {
      Context context = this.context;
      if (downloader == null) {//客户端没有配置自己的Downloader
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {//客户端没有配置自己的memory cahce
        cache = new LruCache(context);//默认是LRU算法的memory cache
      }
      if (service == null) {//客户端没有配置自己的线程池
        service = new PicassoExecutorService();
      }
      if (transformer == null) {//客户端没有配置自己的转换器
        //配置默认的请求转换器,默认是对最初的Request不做转换
        transformer = RequestTransformer.IDENTITY;
      }
      。。。此处暂时省略了重要的代码。。。
    }
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

我们知道Picasso中通过with方法其实是用默认的builder配置来初始化Picasso的单例对象。其实在你调用with方法之前(注意是调用with方法之前),还可以调用setSingletonInstance(Picasso)结合Builder配置自己的单例Picasso对象:

  static volatile Picasso singleton = null;
public static void setSingletonInstance(@NonNull Picasso picasso) {
   ......
    synchronized (Picasso.class) {
      if (singleton != null) {//这个非null判断就意味着setSingletonInstance必须在with方法之前调用
        throw new IllegalStateException("Singleton instance already exists.");
      }
      //初始化单例对象
      singleton = picasso;
    }
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这其实也算是单例模式+Builder模式的灵活应用吧!挺值得学习、借鉴!

load+into的简单说明

跟ImageLoader相同,Picasso也提供了对不同 图片来源(比如assets里面的图片,Filie里面的图片,或者Drawable)的加载处理。不同之处就是ImageLoader对外提供了统一的显示方法,然后由Imageloader自己判断来源从而提供不同的流来获取图片,而Picasso提供了多个重载load方法来处理对应的情况: 
这里写图片描述 
我们就挑拣其中的一个load(Uri)来进行分析吧。 
其实,Picasso的源码分析起来也很简单,层层跟进load方法调用路径就可以了。 
在调用load的时候实际上返回的是一个RequestCreator:

//request的builder对象,此处感觉应该命名为requestBuilder好点
  private final Request.Builder data;
  RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    if (picasso.shutdown) {
      throw new IllegalStateException(
          "Picasso instance already shut down. Cannot submit new requests.");
    }
    this.picasso = picasso;
    //通过构造器初始化request.Builder,并把uri传给builder
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

也就是说在调用load方法的时候实际上并没有对图片资源进行加载,只是简单返回了一个RequestCreator对象,该对象在初始化的时候初始化了Request的Builder对象(好吧,又是Builder模式的应用)。 
使用过Picasso的都知道,我们调用RequestCreator的into方法来完成工作的,那么就先简单的分析RequestCreator的into系列重载方法之一进行说明:

public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    //判断是否是主线程
    checkMain();
    。。。此处有省略代码。。。
    //建立一个请求对象
    Request request = createRequest(started);
    //简历请求的key
    String requestKey = createKey(request);
    。。。。此处有省略代码。。。

    //创建action,实际上以一个ImageViewAction
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);
    //提交Action
    picasso.enqueueAndSubmit(action);
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

上面的代码为了本篇博文的说明也是去掉了一些枝枝蔓蔓,甚至里面不乏重要的代码,在本篇不做论述,后面博文会讲到。into的主要逻辑就是把request+target封装交给action,然后由Picasso对此action进行提交。

Action的简要说明

为什么既然有了Request对象,又会需要一个Action呢?其实仔细分析源码可以发现Request侧重点在于请求本身:比如请求图片资源的uri,对请求的图片做什么处理等等一系列请求,主要是对Image做什么样的展示效果处理等。而Action的所含有的属性如下:

final Picasso picasso;//持有上文单利引用
  final Request request;//上文提到的request
  final WeakReference<T> target;//target可能是ImageView,或者优先理解为ImageView,为弱引用,确保target被回收的时候不受影响
  final boolean noFade;
  final int memoryPolicy;//缓存策略
  final int networkPolicy;//
  final int errorResId;
  final Drawable errorDrawable;
  final String key;
  final Object tag;
  boolean willReplay;
  boolean cancelled;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

很显然Action的主要职责就是:对图片进行加载,配置图片的文件缓存和内存缓存策略以及是否重新加载等逻辑。使得责任分明,调理清晰。 
Action是一个抽象的泛型类,提供了complete和error两个抽象方法、它的子类又如下几个: 
GetAction、FetchAction、ImageViewAction、TargetAction在此处我们提交的是ImageViewAction. 
让我们简单的看一下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 context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    //设置图片
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
     //加载图片完成后执行的操作,有客户端配置自己的callback
    if (callback != null) {
      callback.onSuccess();
    }
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

可以发现最终是由PicassoDrawable来完成图片的显示,所以继续跟进: 
PicasDrawable是BitmapDrawable的子类:

static void setBitmap(ImageView target, Context context, Bitmap bitmap。。) {
    Drawable placeholder = target.getDrawable();//现获取drawable
    if (placeholder instanceof AnimationDrawable) {
      ((AnimationDrawable) placeholder).stop();
    }
    //生成一个dreawable对象
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);//最终图片就这么蹦跶出来了。
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

RequestHandler


该类用来处理不同来源的图片,是个抽象类,功能类似于Imageloader对不同图片来源返回不同的输入流,在picasso只是用面向对象的方式处理来自网络(NetworkRequestHandler),Resoure资源图片(ResourceRequestHandler),asset文件中的图片(AssetRequestHandler)等等若干个Handler。在初始化Picasso的时候,Picasso默认实现的RequestHandler的上述子类是预加载到一个集合中去的!:

 Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {

    List<RequestHandler> allRequestHandlers =
        new ArrayList<RequestHandler>(builtInHandlers + extraCount);
    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
      allRequestHandlers.addAll(extraRequestHandlers);
    }
    //添加默认RequestHandlers
    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);
    }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

可以发现Picasso自己实现的handler在构造器初始化的时候就把自己实现的RequestHander的子类预先初始化好(姑且称之为Picasso内置的ReqeustHandler对象),当然用户也可以实现自己的RequestHandler通过Picasso.Builder.addRequestHandler来添加进去,当然大多数情况不需要自己提供ReqeuestHandler的实现。这种处理方式或者预先初始化的对象放入集合的方式其实在Gson源码中也有所体现。 
那Picasso怎么知道是来自于哪个种类的图片呢?这就是ReqeuestHandler的功能了!RequestHandler时候一个抽象类,从名字上就可以知道它是处理具体请求的,它提供了两个重要的抽象方法来完成对不同种类图片的处理工作: 
abstract canHandleRequest(Request data):判断某子类是否有能力处理当前请求 
abstract load(Request request, int networkPolicy):在canHandleRequest方法返回true的时候,就用该RequestHandler的实现类的load方法来加载图片! 
就让我们先简单分析Picasso怎么处理网络Reqeust的:NetworkRequestHandler

//判断当前请求的图片资源是否来自于服务器或者网络
public boolean canHandleRequest(Request data) {
    String scheme = data.uri.getScheme();
    return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
  }
//NetWorkReqesutHandler对load的处理  
public Result load(Request request, int networkPolicy) throws IOException {
    //下载图片资源,返回一个Response对象
    Response response = downloader.load(request.uri, request.networkPolicy);
    if (response == null) {
      return null;
    }

    Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
    Bitmap bitmap = response.getBitmap();
    if (bitmap != null) {
      return new Result(bitmap, loadedFrom);
    }

    InputStream is = response.getInputStream();
    if (is == null) {
      return null;
    }
     。。。此处有省略代码。。。
    return new Result(is, loadedFrom);
  }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

逻辑很简单,就是在判断当前Reqeuest为网络请求的时候调用load方法从网络中下载图片等处理。

那么ReqeuestHandler什么时候才真正起作用呢?沿着Picasso的脉络分析也很容易发现,在Picasso的Dispatcher在分发请求(从代码上来说应该是分发action)的时候会调用performSubmit方法,在此方法里面会来检测当前请求图片的种类来源,同样砍掉一些枝枝蔓蔓核心代码如下:

void performSubmit(Action action, boolean dismissFailed) {
    //BitmapHunter获取了请求的key,Hunter是一个runnable
    BitmapHunter hunter = hunterMap.get(action.getKey());

    //重新低action进行包装
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    //提交hunter
    hunter.future = service.submit(hunter);
  }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

该方法里面调用了forRequest方法,那么这个方法就是我们要找的方法了!!!

 static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
      Action action) {
    //获取当前请求对象
    Request request = action.getRequest();
    //获取picasso内置的RequestHandler对象和自定义的RequestHandler对象
    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    //判断内置对象或者自定义的对象哪一个对象能处理当前请求
    for (int i = 0, count = requestHandlers.size(); i < count; i++) {
      RequestHandler requestHandler = requestHandlers.get(i);
      if (requestHandler.canHandleRequest(request)) {//如果能处理当前请求
        //返回一个bitMapHandler
        //把当前ReqeuestHandler对象交给BitmapHunter
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
      }
    }
    //说明当前请求无效
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

上面forRequest方法也很简单:主要执行了一下逻辑 
1、获取当前请求Reqeuest对象 
2、获取Picasso内置的RequestHandler以及自定义的RequestHandler集合。 
3、判断集合中哪一个RequestHandler对象可以对当前Reqeuest进行处理(canHanlderReqest返回true) 
4、把集合中能处理当前Request的RequestHandler对象交给BitMapHunter。 
注意此时Picasso并没有立即调用RequestHandler对象的load方法进行处理,而是继续调用 service.submit(hunter);方法来在线程池中对BitmapHunter这个Runnable进行处理,所以我们不难猜测出来在run方法中必然调用了RequestHandler的load方法! 
那么就继续追踪BimapHunter的run方法发现其调用了hunt()方法,那么hunt()方法是由做了什么呢?:

//BitmapHunter的run方法
public void run() { 
    result = hunt();//获取执行结果
 }
//注意该方法返回了一个bitmap
 Bitmap hunt() throws IOException {
    Bitmap bitmap = null;

    //获取读取内存代码省略
    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    //此处真是调用了load方法进行处理
    RequestHandler.Result result = requestH andler.load(data, networkPolicy);
     ....此处省略大量代码....
    return bitmap;
  }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

看到了吧!在hunt()方法中正式调用了forReqeuset过滤的ReqesutHandler对象的load方法完成了核心业务功能!


http://blog.csdn.net/chunqiuwei/article/details/52040064

上面简单的介绍了一下Picasso的工作流程,而且故意省略了很多重要的地方因为篇幅需要暂时没做说明,会继续写博客慢慢抽丝拨茧分析,其实阅读源码很枯燥,有好多自己能体会到的地方有时候因为语言组织能力有限,只能自己体会而没办法写出来,Picasso的代码思路也很清晰,建议在业余的时间读读加深体会也是好的,好多东西自己钻研了才会真正的获得属于自己的收获。







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值