picasso_picasso-强大的android图片下载缓存库 - CSDN
精华内容
参与话题
  • android Picasso使用详解

    千次阅读 2018-05-22 08:47:12
    Picasso是Square公司出品的一款非常优秀的开源图片加载库,是目前Android开发中超级流行的图片加载库之一,今天我们就来分析下他的使用及实现流程。 使用简介 首先在项目中引入picasso(以gradle为例) compile '...


    Picasso是Square公司出品的一款非常优秀的开源图片加载库,是目前Android开发中超级流行的图片加载库之一,今天我们就来分析下他的使用及实现流程。

    使用简介

    首先在项目中引入picasso(以gradle为例)
    compile 'com.squareup.picasso:picasso:2.5.2'
    传统的ImageVIew设置图片
    Picasso.with(context).load(url).placeholder(R.drawable.tab_item_bg).into(imageView);
    自定义的布局设置图片,target是指实现了Target接口的自定义View
    Picasso.with(context).load(url).placeholder(R.drawable.tab_item_bg).into(target);
    adapter中的使用
    //View复用会自动察觉并且取消之前的下载任务
    @Override 
    public void getView(int position, View convertView, ViewGroup parent) { 
        SquaredImageView view = (SquaredImageView) convertView; 
        if (view == ) { 
            view = new SquaredImageView(context); 
        } 
        String url = getItem(position); 
        Picasso.with(context).load(url).into(view);
      }
    自动设置图片宽高像素的大小
    Picasso
        .with(context) 
        .load(url) 
        .resize(50, 50) 
        .centerCrop() 
        .into(imageView)

    流程分析

    接下来我们就以上面的链式调用来分析Picasso的实现流程,首先来认识几个类

    RequestHandler

    //抽象类,由不同的子类来实现不同来源的图片的获取与加载,比如:
    //AssetRequestHandler:加载asset里的图片
    //FileRequestHandler:加载硬盘里的图片
    //ResourceRequestHandler:加载资源图片
    //NetworkRequestHandler:加载网络图片

    BitmapHunter

    //是一个Runnable的子类,用来进行Bitmap的获取(网络,硬盘,内存等),处理(角度,大小等),
    //然后执行分发器(dispatcher)的回调处理

    PicassoDrawable

    //实现了引入图片渐变功能和debug状态的标识的Drawable,用来在最后bitmap转换成PicassoDrawable
    //然后设置给ImageView,根据图片的来源可以在图片的左上角显示一个不同颜色的三角形色块
    MEMORY(Color.GREEN) //内存加载  
    DISK(Color.BLUE)    //本地加载
    NETWORK(Color.RED)  //网络加载

    DeferredRequestCreator

    //ViewTreeObserver.OnPreDrawListener的实现类,即将绘制视图树时执行的回调函数。
    //这时所有的视图都测量完成并确定了框架。 客户端可以使用该方法来调整滚动边框,
    //甚至可以在绘制之前请求新的布局,这里用来实现当开发者修改图片尺寸时的逻辑

    Action

    //Action代表了一个具体的加载任务,主要用于图片加载后的结果回调,有两个抽象方法,complete和error,
    //并保存了每个请求的各种信息(),具体的实现类如下
    //GetAction:同步执行请求时使用。
    //FetchAction:当不需要ImageView来安置bitmap时的异步请求,通常用来预热缓存
    //RemoteViewsAction:用来更新远程图片(notification等)的抽象类。
    //TargetAction:一般在View(不只是ImageView)或者ViewHolder中用来加载图片,需要实现Target接口
    //ImageViewAction:最常用的Action,主要用来给ImageView加载图片

    流程分析

    1. Picasso对象的创建
    //通过Picasso的一个静态内部类Builder来创建本身的对象(单例)
    //这种构建方式在很多的流行开源库中都在使用,可以借鉴
    public static Picasso with(Context context) {  
      if (singleton == ) {    
        synchronized (Picasso.class) {      
            if (singleton == ) {        
                singleton = new Builder(context).build();      
                }    
            }  
        }  
      return singleton;
    }
    
    public Picasso build() {    
      Context context = this.context;   
      //下载器 
      if (downloader == ) {      
          downloader = Utils.createDefaultDownloader(context);    
        }   
      //Lru缓存
      if (cache == ) {      
          cache = new LruCache(context);    
        }   
      //线程池,核心线程数为3,使用优先队列
      if (service == ) {      
          service = new PicassoExecutorService();    
        }    
      //请求的前置处理,在请求发出去之前执行,类似于拦截器
      if (transformer == ) {      
          transformer = RequestTransformer.IDENTITY;    
        }    
      //状态控制类,用来发送各种消息,例如查找图片缓存的结果(击中/未击中),下载完成等
      Stats stats = new Stats(cache);  
    //分发器,用来分发任务  
    Dispatcher dispatcher = new Dispatcher(
      context, service, HANDLER, downloader, cache, stats);   
     //返回一个Picasso对象
    return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,        defaultBitmapConfig, indicatorsEnabled, loggingEnabled);  }}
    2. 加载url,创建并返回一个图片下载请求的构建器RequestCreator
    //不仅仅用来存储图片的URL,
    public RequestCreator load(String path) {  
     if (path == ) {    
        return new RequestCreator(this, , 0);  
      }    
     if (path.trim().length() == 0) {    
      throw new IllegalArgumentException("Path must not be empty.");  
      }  
      return load(Uri.parse(path));
    }
    
    
    
    //请求构建器,用来存储该图片加载任务的一切信息,比如:
    //目标的宽高,图片的显示样式(centerCrop,centerInside),旋转角度和旋转点坐标,以及图片
    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;  
      this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
      }
    
    
    //对图片设置矛盾之处的校验,校验通过则返回Request对象,该方法在最初方法链的into方法中(createRequest)调用
    public Request build() {    
     if (centerInside && centerCrop) {      
        throw new IllegalStateException("Center crop and center inside can not be used together.");    
      }    
    if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {      
        throw new IllegalStateException("Center crop requires calling resize with positive width and height.");    
     }    
    if (centerInside && (targetWidth == 0 && targetHeight == 0)) {      
        throw new IllegalStateException("Center inside requires calling resize with positive width and height.");    
    }    
    //默认的优先级为普通
    if (priority == ) {      
        priority = Priority.NORMAL;    
    }    
    return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,        
        centerCrop, centerInside, onlyScaleDown, rotationDegrees, rotationPivotX, rotationPivotY,        
        hasRotationPivot, purgeable, config, priority);  }}
    3. 设置默认图片及出错图片
    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 != ) {    
          throw new IllegalStateException("Placeholder image already set.");  
        }  
        this.placeholderResId = placeholderResId;  
      return this;  
    }
    //出错图片的处理逻辑与之类似
    4. 修改图片的尺寸,填充图片进ImageView
    public void into(ImageView target, Callback callback) {  
      long started = System.nanoTime();
      //线程检查  
      checkMain();  
      if (target == ) {    
        throw new IllegalArgumentException("Target must not be null.");  
      }  
      //没设置url以及resId则取消请求
      if (!data.hasImage()) {    
        picasso.cancelRequest(target);    
        if (setPlaceholder) {      
          setPlaceholder(target, getPlaceholderDrawable());    
          }    
        return;  
      }  
      //仅有fit()方法会修改该flag为true,但是该方法只能由开发者显式调用,因此下面的代码默认是不会执行的
      if (deferred) {//当尝试调整图片的大小让其正好适合ImageView时defered为true(fit()方法)
        if (data.hasSize()) {      
            //判断ImageView大小,即如果ImageView已经渲染完成,则无法改变大小
            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());      
            }      
            //TODO:
            picasso.defer(target, new DeferredRequestCreator(this, target, callback));      
            return;    
          }      
        data.resize(width, height);  
      }  
    //Request的拦截器:
    //简单介绍下。他会对原始的Request做一个拦截,看是否需要做处理。
    //比如说使用CDN的时候修改主机名以达到更快的访问速度等操作。
    Request request = createRequest(started);  
    //根据请求的URL,图片的处理等参数创建一个字符串Key
    String requestKey = createKey(request);
     //缓存策略,是否应该从内存中读取 
     if (shouldReadFromMemoryCache(memoryPolicy)) {
        //内存的快速检查,    
        Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);    
      if (bitmap != ) { 
          //如果缓存中已存在则取消请求并直接设置给ImageView     
          picasso.cancelRequest(target);      
          setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);      
          ...//日志     
          if (callback != ) {        
            callback.onSuccess();      
          }      
        return;    
      }  
    }
    if (setPlaceholder) {  
      setPlaceholder(target, getPlaceholderDrawable());
    }
    //这里创建的是ImageViewAction对象,后面会用到
    Action action = new ImageViewAction(picasso, target, request, 
    memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);
    //提交请求,Picasso内部维护了一个map,key是imageView,value是Action
    //提交时做判断,如果当前imageView已经在任务队列里了。判断当前任务与之前的任务是否相同,
    //如果不相同则取消之前的任务并将新的key-value加入到map
    picasso.enqueueAndSubmit(action);
    }
    5. Dispatcher(任务分发器)会通过Handler来提交任务,然后交由Dispatcher的performSubmit方法来执行
    void performSubmit(Action action, boolean dismissFailed) {    
      if (pausedTags.contains(action.getTag())) {    
          pausedActions.put(action.getTarget(), action);      
          ...     
          return;    
      }    
      //查看是否有对应url的缓存的hunter
      //BitmapHunter的简介请看文章开始的介绍
      BitmapHunter hunter = hunterMap.get(action.getKey());    
      if (hunter != null) {      
        hunter.attach(action);      
        return;    
      }    
      if (service.isShutdown()) {
        ...     
        return;    
      }      
      hunter = forRequest(action.getPicasso(), this, cache, stats, action);  
      //提交任务到线程池
      hunter.future = service.submit(hunter);    
      //将runnable缓存在map集合中
      hunterMap.put(action.getKey(), hunter);    
      if (dismissFailed) {      
          failedActions.remove(action.getTarget());    
        }
        ...
      }
    6.根据不同的加载路径,选择合适的RequestHandler来创建BitmapHunter
    static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {    
        Request request = action.getRequest();  
        //Picasso中默认保存了一个集合,里面存储了每一类图片的加载,判断和处理逻辑 ,比如网络图片,资源图片,硬盘图片,照片等 
        List<RequestHandler> requestHandlers = picasso.getRequestHandlers();    
        // Index-based loop to avoid allocating an iterator.    
        //noinspection ForLoopReplaceableByForEach    
        for (int i = 0, count = requestHandlers.size(); i < count; i++) { 
          RequestHandler requestHandler = requestHandlers.get(i);   
          //它会通过request中url的scheme来判断该使用哪一个RequestHandler     
          if (requestHandler.canHandleRequest(request)) {          
              return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);      
          } 
        }    
      return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);}
    7. BitmapHunter被提交到线程池之后,接下来就该run方法执行了
    @Override   
    public void run() {    
      try {      
      updateThreadName(data);
      ...     
      }      
      //重点!!!
      result = hunt(); 
      //得到bitmap后,执行Dispater中的方法     
      if (result == ) {        
        dispatcher.dispatchFailed(this);      
      } else {        
        dispatcher.dispatchComplete(this);      
      }    
    } catch (Downloader.ResponseException e) {      
        if (!e.localCacheOnly || e.responseCode != 504) {        
          ...   
        }      
        dispatcher.dispatchFailed(this);    
     } catch (IOException e) {//重试      
        ...      
        dispatcher.dispatchRetry(this);    
      } catch (OutOfMemoryError e) {//OOM异常的处理      
          StringWriter writer = new StringWriter();      
          stats.createSnapshot().dump(new PrintWriter(writer));      
          exception = new RuntimeException(writer.toString(), e);  
          dispatcher.dispatchFailed(this);    
      } catch (Exception e) {      
          ...      
          dispatcher.dispatchFailed(this);    
      } finally {      
           ...       
        }    
     }
    8. 重点:bitmap的获取(包括获取途径(内存,硬盘,网络)的判断以及加载)
    Bitmap hunt() throws IOException {    
      Bitmap bitmap = ;    
      //是否从内存读取    
      if (shouldReadFromMemoryCache(memoryPolicy)) {      
        bitmap = cache.get(key);      
        if (bitmap != ) { 
          //发送一个内存缓存中查找成功的消息       
          stats.dispatchCacheHit();        
          loadedFrom = MEMORY;
          ...
          return bitmap;      
          }    
        }    
      //NO_CACHE:跳过检查硬盘缓存,强制从网络获取
      //NO_STORE:不存储到本地硬盘
      //OFFLINE:只从本地硬盘获取,不走网络
    
      //如果重试次数为0则走本地硬盘,否则从网络获取
      data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;    
      //根据重试的次数和不同的requestHandler的子类来实现不同来源图片的加载,资源文件,硬盘图片又或者网络图片
      //硬盘及网络资源的加载逻辑,具体实现在下面
      RequestHandler.Result result = requestHandler.load(data, networkPolicy);    
      if (result != ) {      
        //加载途径(硬盘,内存,网络)
        loadedFrom = result.getLoadedFrom();      
        //
        exifOrientation = result.getExifOrientation();      
        bitmap = result.getBitmap();      
        // 返回的resuslt中包括bitmap和inputstream,如果bitmap为null则需要自己从stream中转换
        if (bitmap == ) {        
          InputStream is = result.getStream();        
          try {          
            bitmap = decodeStream(is, data);        
           } finally {          
            Utils.closeQuietly(is);        
             }      
          }    
        }    
        if (bitmap != ) {
         ...   
        //修改stats中记录的图片的个数和占用的内存总大小以及平均内存占用量 
        stats.dispatchBitmapDecoded(bitmap); 
          //图片是否需要旋转或者其他的操作处理     
          if (data.needsTransformation() || exifOrientation != 0) {   
             synchronized (DECODE_LOCK) {            
                if (data.needsMatrixTransform() || exifOrientation != 0) {         
                   bitmap = transformResult(data, bitmap, exifOrientation);
                   ...      
                  }      
                //自定义的图片处理    
                if (data.hasCustomTransformations()) {            
                   bitmap = applyCustomTransformations(data.transformations, bitmap);
                  ...     
                  }        
              }        
            //记录下图片处理的次数以及处理后的占用的总内存大小以及每张图片的平均内存占用量
            if (bitmap != ) {       
               stats.dispatchBitmapTransformed(bitmap);        
              }      
            }    
          }    
      return bitmap;  
    }
    9.以网络图片为例介绍下从硬盘和网络加载图片的流程
    @Override 
    public Response load(Uri uri, int networkPolicy) throws IOException {  
      CacheControl cacheControl = null;  
      if (networkPolicy != 0) {    
        //只走本地缓存
        if (NetworkPolicy.isOfflineOnly(networkPolicy)) {      
            cacheControl = CacheControl.FORCE_CACHE;    
          } else {   
            //自定义缓存策略   
            CacheControl.Builder builder = new CacheControl.Builder();      
            //不从硬盘读
            if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {   
               builder.noCache();      
              }      
            //不写入硬盘
            if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) { 
               builder.noStore();      
             }      
            cacheControl = builder.build();    
         }  
      }  
      Request.Builder builder = new Request.Builder().url(uri.toString());  
      if (cacheControl != null) {    
        builder.cacheControl(cacheControl);  
      }  
      //重点:调用okhttp或者UrlConnection来执行bitmap的下载任务(拦截器)。
      okhttp3.Response response = client.newCall(builder.build()).execute();  
      int responseCode = response.code();  
      if (responseCode >= 300) {    
         response.body().close();    
        throw new ResponseException(responseCode + " " + response.message(), networkPolicy,        responseCode);  
        }  
      boolean fromCache = response.cacheResponse() != ;  
      ResponseBody responseBody = response.body();  
      return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
     }
    10. 当从硬盘或者服务器获取Bitmap之后,就可以通过Action来执行各种自定义的callback了
    //dispatcher.dispatchComplete()用来处理bitmap成功获取后的逻辑,
    //经过一系列的转发,最终逻辑是在Picasso类里的complete方法中执行的。
    void complete(BitmapHunter hunter) {  
      Action single = hunter.getAction();    
      List<Action> joined = hunter.getActions();  
      boolean hasMultiple = joined !=  && !joined.isEmpty();  
      boolean shouldDeliver = single !=  || hasMultiple;  
      if (!shouldDeliver) {    
          return;  
      }  
      Uri uri = hunter.getData().uri;  
      Exception exception = hunter.getException();  
      Bitmap result = hunter.getResult();  
      LoadedFrom from = hunter.getLoadedFrom();  
      if (single != ) {    
        deliverAction(result, from, single);  
      }  
      if (hasMultiple) {    
        //noinspection ForLoopReplaceableByForEach    
        for (int i = 0, n = joined.size(); i < n; i++) {      
          Action join = joined.get(i);      
          deliverAction(result, from, join);    
        }  
      }  
      if (listener !=  && exception != ) {   
         listener.onImageLoadFailed(this, uri, exception);  
        }
     }
    
    
    private void deliverAction(Bitmap result, LoadedFrom from, Action action) {    
      if (action.isCancelled()) {      
        return;    
      }    
      if (!action.willReplay()) {      
        targetToAction.remove(action.getTarget());    
      }    
      if (result != ) {      
        if (from == ) {        
            throw new AssertionError("LoadedFrom cannot be null.");      
        }     
        //这里的action就是前面提到的ImageViewAction,其他还有RemoteViewsAction,TargetAction等 
        action.complete(result, from);
        ...   
      } else {      
        action.error();
        ... 
      }  
    }
    以ImageViewAction为例看下complete中的实现
    @Override   
    public void complete(Bitmap result, Picasso.LoadedFrom from) {    
      if (result == ) {    
        throw new AssertionError(String.format("Attempted to complete action with no result!\n%s", this));  
      }  
      ImageView target = this.target.get();  
      if (target == ) {    
        return;  
      }  
      Context context = picasso.context;  
      //是否展示来源的标签,默认不展示。
      boolean indicatorsEnabled = picasso.indicatorsEnabled;
      PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);  
      if (callback != ) {    
        callback.onSuccess();  
      }
    }
    //PicassoDrawable中setBitmap方法的实现
    static void setBitmap(ImageView target, Context context, Bitmap bitmap,    
            Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) { 
     Drawable placeholder = target.getDrawable();  
     if (placeholder instanceof AnimationDrawable) {    
        ((AnimationDrawable) placeholder).stop();  
      }  
     //最终扔到ImageView上现实的的是PicassoDrawable
     PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);  
     target.setImageDrawable(drawable);}

    到这里,Picasso加载图片的逻辑就分析完了。下面我们看下Square还留给我们什么其他可以学习的东西。

    Picasso的引用清理策略

    Picasso的缓存是对请求的缓存,通过WeakReference与ReferenceQueue的联合使用来缓存请求,然后
    关于ReferenceQueue请看下文的详细介绍
    WeakReference与ReferenceQueue联合使用构建java高速缓存

    //1.首先通过RequestWeakReference来弱化Action的引用强度
    static class RequestWeakReference<M> extends WeakReference<M> {  
      final Action action;  public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {    
        super(referent, q);    
        this.action = action;  
        }
    }
    
    //2.其次通过ReferenceQueue来存放引用
    Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,  
      int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {  
        ...
        this.target = target ==  ? 
             : new RequestWeakReference<T>(this, target, picasso.referenceQueue);  
            ...
      }
    
    
    //3.通过Picasso内部的CleanupThread来清理ReferenceQueue中的引用。
    @Override 
    public void run() {  
      Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);  
      while (true) {    
       try {      
        // Prior to Android 5.0, even when there is no local variable, the result from      
        // remove() & obtainMessage() is kept as a stack local variable.      
        // We're forcing this reference to be cleared and replaced by looping every second      
        // when there is nothing to do.      
        // This behavior has been tested and reproduced with heap dumps.    
        RequestWeakReference<?> remove = (RequestWeakReference<?>) referenceQueue.remove(THREAD_LEAK_CLEANING_MS);      
        Message message = handler.obtainMessage();      
        if (remove != ) {        
            message.what = REQUEST_GCED;        
            message.obj = remove.action;        
            handler.sendMessage(message);      
        } else {        
            message.recycle();      
          }  
        } catch (InterruptedException e) {     
           break;    
        } catch (final Exception e) {      
          handler.post(new Runnable() {        
              @Override 
              public void run() {          
                  throw new RuntimeException(e);      
                }     
             });      
        break;   
         }  
        }
      }

    Picasso优先级策略

    请求的优先级
    public enum Priority {  
      LOW,  // 只有当通过fetch方法(不需要ImageView,仅需要下载Bitmap并执行回调)请求图片时才会给request设置该优先级
      NORMAL,// 正常的请求优先级 
      HIGH//
     }
    图片下载任务的优先级

    真实的项目开发过程中,只请求图片而不设置给ImageView毕竟是少数,所以大多数的任务依然会是NORMAL级别的,同级别的任务数太多,那么优先级策略就没有太大的效果,所以,Picasso在执行图片下载任务时,又做了第二次优先级划分

    1. 依赖于优先级队列的线程池
    PicassoExecutorService() {  
       super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,      
       new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
    }
    2. 实现了Comparable接口的FutureTask
    private static final class PicassoFutureTask extends FutureTask<BitmapHunter>   
               implements Comparable<PicassoFutureTask> {  
      private final BitmapHunter hunter;  
    
      public PicassoFutureTask(BitmapHunter hunter) {    
        super(hunter, );    
        this.hunter = hunter;  
      }  
      @Override  
      public int compareTo(PicassoFutureTask other) {    
        Picasso.Priority p1 = hunter.getPriority();    
        Picasso.Priority p2 = other.hunter.getPriority();    
        //高优先级的请求放到队列前面
        //同等级的请求按照顺序先后执行
        return (p1 == p2 ? hunter.sequence - other.hunter.sequence : 
              p2.ordinal() - p1.ordinal());  
         }
      }

    PicassoExecutorService(Picasso自己封装的线程池,对移动网络做了处理,并提供了支持优先级比较的FutureTask)

    //这段代码我们在将来的开发中可能会用得上,包含了国内每种移动网络
    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);  
        }
      }

    OKHttp的拦截器链设计模式的应用(简介)

    当执行如下代码时,如果底层是通过OKHttp来请求图片,会先执行OKHttp自带的拦截器中的方法,拦截器中的逻辑执行完之后才会执行真正的图片请求

    okhttp3.Response response = client.newCall(builder.build()).execute();

    看一下Call接口的实现类RealCall中execute方法的具体实现。

    @Override 
    public Response execute() throws IOException {  
      ...
      try {        
        //开始执行拦截器链
        Response result = getResponseWithInterceptorChain(false);    
        ...
        return result;  
       } finally {    
        client.dispatcher().finished(this);  
        }
      }
    
    
    private Response getResponseWithInterceptorChain(boolean forWebSocket) 
          throws IOException {  
        Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);  
        return chain.proceed(originalRequest);
      }
    
    class ApplicationInterceptorChain implements Interceptor.Chain {  
        private final int index;  //拦截器的计数器,表示当前需要执行第几个拦截器中的方法。
        ...
     ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {   
         this.index = index;        
         ...
        }  
      ...
     @Override 
     public Response proceed(Request request) throws IOException {    
    
      //判断是否超出拦截器集合的下标
      if (index < client.interceptors().size()) {      
        //在Picasso中,维护了一个拦截器的集合,这里通过对集合内拦截器的下标索引
        //来依次获取开发者所定义的拦截器
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);      
        Interceptor interceptor = client.interceptors().get(index);   
        //然后依次调用拦截方法   
        Response interceptedResponse = interceptor.intercept(chain);      
        if (interceptedResponse == ) {        
            throw new NullPointerException("application interceptor " + interceptor + " returned null");      
          }      
          return interceptedResponse;    
      }    
      // 拦截器执行完了。就开始执行真正的网络请求了。
      return getResponse(request, forWebSocket);  
     }  
    }

    到这里,Picasso的使用介绍及流程分析就全部介绍完了。



                </div>
    
    展开全文
  • 写在前面Android 中有几个比较有名的图片加载框架,Universal ImageLoader、Picasso、Glide和Fresco。它们各有优点,以前一直用的是ImageLoader 做项目中的图片加载,由于作者宣布ImageLoader 不会在更新了,因此新...

    写在前面

    Android 中有几个比较有名的图片加载框架,Universal ImageLoader、Picasso、Glide和Fresco。它们各有优点,以前一直用的是ImageLoader 做项目中的图片加载,由于作者宣布ImageLoader 不会在更新了,因此新的项目打算换一个图片加载框架-Picasso, Picasso 是Square 公司开源的Android 端的图片加载和缓存框架。Square 真是一家良心公司啊,为我们Android开发者贡献了很多优秀的开源项目有木有!像什么Rerefoit 、OkHttp、LeakCanary、Picasso等等都是非常火的开源项目。扯远了,回到正题,除了使用简单方便,Picasso还能自动帮我们做以下事情:

    • 处理Adapter 中ImageView的回收和取消下载。
    • 使用最小的内存 来做复杂的图片变换。比如高斯模糊,圆角、圆形等处理。
    • 自动帮我们缓存图片。内存和磁盘缓存。

    以上只是列出了Picasso 比较核心的几点,其实它的优点远远不止这些,接下来就看一下如何使用Picasso。

    Picasso-Android.png

    本文目录

    0,添加依赖
    1, 加载显示图片
    2,Placeholder & noPlaceholder & noFade
    3, 设置图片尺寸(Resize)、缩放(Scale)和裁剪(Crop)
    4,图片旋转Rotation()
    5, 转换器Transformation
    6,请求优先级
    7,Tag管理请求
    8,同步/异步加载图片
    9,缓存(Disk 和 Memory)
    10,Debug 和日志
    11,Picasso 扩展

    正文

    0. 添加依赖

    要使用Picasso,首先我们要添加版本依赖,去官网或者Github 看一下当前的最新版本(截止本文最新版本为2.5.2),然后在build.gradle中添加依赖:

       compile 'com.squareup.picasso:picasso:2.5.2'
    

    1. 加载显示图片

    将Picasso添加到项目之后,我们就可以用它来加载图片了,使用方法非常简单:

     Picasso.with(this)
            .load("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg")
            .into(mImageView);
    

    只需要一行代码就完成了加载图片到显示的整个过程,链式调用,非常简洁,其实有三步,一次调用了三个方法:

    • with(Context) 获取一个Picasso单例,参数是一个Context上下文
    • load(String) 调用load 方法加载图片
    • into (ImageView) 将图片显示在对应的View上,可以是ImageView,也可以是实现了Target j接口的自定义View。

    上面演示了加载一张网络图片,它还支持其它形式的图片加载,加载文件图片,加载本地资源图片,加载一个Uri 路径给的图片,提供了几个重载的方法:

    1, load(Uri uri) 加载一个以Uri路径给的图片

    Uri uri = Uri.parse(ANDROID_RESOURCE + context.getPackageName() + FOREWARD_SLASH + resourceId)
    
    Picasso.with(this).load(uri).into(mImageView);
    

    ** 2,load(File file) 加载File中的图片**

     Picasso.with(this).load(file).into(mImageView);
    

    3, load(int resourceId) 加载本地资源图片

    Picasso.with(this).load(R.mipmap.ic_launcher).into(mImageView);
    

    提醒:上面介绍了load的几个重载方法,加载不同资源的图片,另外提醒注意一下load(String path)接受String 参数的这个方法,参数String 可以是一个网络图片url,也可以是file 路径、content资源 和Android Resource。看一下源码的注释。

    /**
       * Start an image request using the specified path. This is a convenience method for calling
       * {@link #load(Uri)}.
       * <p>
       * This path may be a remote URL, file resource (prefixed with {@code file:}), content resource
       * (prefixed with {@code content:}), or android resource (prefixed with {@code
       * android.resource:}.
       * <p>
       * Passing {@code null} as a {@code path} will not trigger any request but will set a
       * placeholder, if one is specified.
       *
       * @see #load(Uri)
       * @see #load(File)
       * @see #load(int)
       * @throws IllegalArgumentException if {@code path} is empty or blank string.
       */
      public RequestCreator load(String path) {
        if (path == null) {
          return new RequestCreator(this, null, 0);
        }
        if (path.trim().length() == 0) {
          throw new IllegalArgumentException("Path must not be empty.");
        }
        return load(Uri.parse(path));
      }
    

    要使用string 参数加载上面的几种资源,除了网络url,其它几种需要加上对应前缀,file文件路径前缀:file: , content 添加前缀:content: ,Android Resource 添加:android.resource:

    2. placeholder& error & noPlaceholder & noFade

    通过上面的第一步我们就可以通过Picasso 加载图片了,我们的项目中通常最常用的就是加载网络图片,但是由于网络环境的差异,有时侯加载网络图片的过程有点慢,这样界面上就会显示空ImageView什么也看不见,用户体验非常不好。其实以前用过ImageLoader的同学都知道,ImageLoader 是可以设置加载中显示默认图片的,Picasso当然也给我们提供了这个功能,这就是我们要说的placeholder(占位图)。

    1,placeholder
    placeholder提供一张在网络请求还没有完成时显示的图片,它必须是本地图片,代码如下:

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .into(mImageView);
    

    设置placeholder之后,在加载图片的时候,就可以显示设置的默认图了,提升用户体验。
    2, error
    和placeholder 的用法一样,error 提供一张在加载图片出错的情况下显示的默认图

    
            Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .into(mImageView);
    
    

    3,noPlaceholder
    这个方法的意思就是:在调用into的时候明确告诉你没有占位图设置。根据这个方法签名的解释是阻止View被回收的时候Picasso清空target或者设置一个应用的占位图。需要注意的是placeholder和noPlaceholder 不能同时应用在同一个请求上,会抛异常。

    
            Picasso.with(this).load(URL)
                    .noPlaceholder()
                    .error(R.drawable.error_iamge)
                    .into(mImageView);
    
    

    4,noFade
    无论你是否设置了占位图,Picasso 从磁盘或者网络加载图片时,into 显示到ImageView 都会有一个简单的渐入过度效果,让你的UI视觉效果更柔顺丝滑一点,如果你不要这个渐入的效果(没有这么坑爹的需求吧!!!),就调用noFade方法。

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .noFade()
                    .into(mImageView);
    

    3. 设置图片尺寸(Resize)、缩放(Scale)和裁剪(Crop)

    1, Resize(int w,int h)
    在项目中,为了带宽、内存使用和下载速度等考虑,服务端给我们的图片的size 应该和我们View 实际的size一样的,但是实际情况并非如此,服务端可能给我们一些奇怪的尺寸的图片,我们可以使用resize(int w,int hei) 来重新设置尺寸。

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .resize(400,200)
                    .into(mImageView);
    

    resize()方法接受的参数的单位是pixels,还有一个可以设置dp单位的方法,将你的尺寸写在dimens.xml文件中,然后用resizeDimen(int targetWidthResId, int targetHeightResId)方法

     <dimen name="image_width">300dp</dimen>
     <dimen name="image_height">200dp</dimen>
    
     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .resizeDimen(R.dimen.image_width,R.dimen.image_height)
                    .into(mImageView);
    

    2,onlyScaleDown
    当调用了resize 方法重新设置图片尺寸的时候,调用onlyScaleDown 方法,只有当原始图片的尺寸大于我们指定的尺寸时,resize才起作用,如:

      Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .resize(4000,2000)
                    .onlyScaleDown()
                    .into(mImageView);
    

    只有当原来的图片尺寸大于4000 x 2000的时候,resize 才起作用。
    3,图片裁剪 centerCrop()
    这个属性应该不陌生吧!ImageView 的ScaleType 就有这个属性。当我们使用resize 来重新设置图片的尺寸的时候,你会发现有些图片拉伸或者扭曲了(使用ImageView的时候碰到过吧),我要避免这种情况,Picasso 同样给我们提供了一个方法,centerCrop,充满ImageView 的边界,居中裁剪

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .resize(400,200)
                    .centerCrop()
                    .into(mImageView);
    

    4,centerInside
    上面的centerCrop是可能看不到全部图片的,如果你想让View将图片展示完全,可以用centerInside,但是如果图片尺寸小于View尺寸的话,是不能充满View边界的。

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .resize(400,200)
                    .centerInside()
                    .into(mImageView);
    

    5,fit
    fit 是干什的呢?上面我们需要用resize()来指定我们需要的图片的尺寸,那就是说在程序中需要我们计算我们需要的尺寸(固定大小的除外),这样很麻烦,fit 方法就帮我们解决了这个问题。fit 它会自动测量我们的View的大小,然后内部调用reszie方法把图片裁剪到View的大小,这就帮我们做了计算size和调用resize 这2步。非常方便。代码如下:

    Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .fit()
                    .into(mImageView);
    

    使用fit 还是会出现拉伸扭曲的情况,因此最好配合前面的centerCrop使用,代码如下:

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .fit()
                    .centerCrop()
                    .into(mImageView);
    

    看一下对比图:
    fit(会拉伸):

    image_fit.png

    fit & centerCrop (不会拉伸):

    fit_centerCrop.png

    注意:特别注意,
    1,fit 只对ImageView 有效
    2,使用fit时,ImageView 宽和高不能为wrap_content,很好理解,因为它要测量宽高。

    4. 图片旋转Rotation()

    在图片显示到ImageView 之前,还可以对图片做一些旋转操作,调用rotate(int degree)方法

    Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .rotate(180)
                    .into(mImageView);
    

    这个方法它是以(0,0)点旋转,但是有些时候我们并不想以(0,0)点旋转,还提供了另外一个方法可以指定原点:

    • rotate(float degrees, float pivotX, float pivotY) 以(pivotX, pivotY)为原点旋转
     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .rotate(180,200,100)
                    .into(mImageView);
    

    5. 转换器Transformation

    Transformation 这就是Picasso的一个非常强大的功能了,它允许你在load图片 -> into ImageView 中间这个过成对图片做一系列的变换。比如你要做图片高斯模糊、添加圆角、做度灰处理、圆形图片等等都可以通过Transformation来完成。

    来看一个高斯模糊的例子:

    1,首先定义一个转换器继承 Transformation

     public static class BlurTransformation implements Transformation{
    
            RenderScript rs;
    
            public BlurTransformation(Context context) {
                super();
                rs = RenderScript.create(context);
            }
    
            @Override
            public Bitmap transform(Bitmap bitmap) {
                // Create another bitmap that will hold the results of the filter.
                Bitmap blurredBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
    
                // Allocate memory for Renderscript to work with
                Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
                Allocation output = Allocation.createTyped(rs, input.getType());
    
                // Load up an instance of the specific script that we want to use.
                ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
                script.setInput(input);
    
                // Set the blur radius
                script.setRadius(25);
    
                // Start the ScriptIntrinisicBlur
                script.forEach(output);
    
                // Copy the output to the blurred bitmap
                output.copyTo(blurredBitmap);
    
                bitmap.recycle();
    
                return blurredBitmap;
            }
    
            @Override
            public String key() {
                return "blur";
            }
        }
    

    2, 加载图片的时候,在into 方法前面调用 transform方法 应用Transformation

      Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .transform(new BlurTransformation(this))
                    .into(mBlurImage);
    

    看一下效果图:

    transformation.png

    上面为原图,下面为高斯模糊图

    是不是很强大,任何复杂的变换都可以通过Transformation 来做。

    还不止于此,还有更强大的功能。可以在一个请求上应用多个Transformation

    比如:我想先做个度灰处理然后在做一个高斯模糊图:

    1, 度灰的Transformation

     public static class GrayTransformation implements Transformation{
    
            @Override
            public Bitmap transform(Bitmap source) {
                int width, height;
                height = source.getHeight();
                width = source.getWidth();
    
                Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                Canvas c = new Canvas(bmpGrayscale);
                Paint paint = new Paint();
                ColorMatrix cm = new ColorMatrix();
                cm.setSaturation(0);
                ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
                paint.setColorFilter(f);
                c.drawBitmap(source, 0, 0, paint);
    
                if(source!=null && source!=bmpGrayscale){
                    source.recycle();
                }
                return bmpGrayscale;
            }
    
            @Override
            public String key() {
                return "gray";
            }
        }
    

    2, 如果是多个Transformation操作,有2种方式应用
    方式一:直接调用多次transform 方法,不会覆盖的。它只是保存到了一个List 里面

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .fit()
                    .centerCrop()
                    .transform(new GrayTransformation())//度灰处理
                    .transform(new BlurTransformation(this))//高斯模糊
                    .into(mBlurImage);
    

    需要注意调用的顺序
    方式二:接受一个List,将Transformation 放大list 里

            List<Transformation> transformations = new ArrayList<>();
            transformations.add(new GrayTransformation());
            transformations.add(new BlurTransformation(this));
    
            Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .fit()
                    .centerCrop()
                    .transform(transformations)
                    .into(mBlurImage);
    

    效果图:

    gray_blur.png

    如上图,第一张为度灰操作,第二张为 度灰+高斯模糊

    另外发现了一个开源库,专门写了很多好玩的Transformation,有兴趣的可以看一下:
    picasso-transformations

    6. 请求优先级

    Picasso 为请求设置有优先级,有三种优先级,LOW、NORMAL、HIGH。默认情况下都是NORMAL,除了调用fetch 方法,fetch 方法的优先级是LOW。

    public enum Priority {
        LOW,
        NORMAL,
        HIGH
      }
    

    可以通过priority方法设置请求的优先级,这会影响请求的执行顺序,但是这是不能保证的,它只会往高的优先级靠拢。代码如下:

     Picasso.with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .priority(Picasso.Priority.HIGH)
                   // .priority(Picasso.Priority.LOW)
                    .into(mImageView);
    

    7. Tag管理请求

    Picasso 允许我们为一个请求设置tag来管理请求,看一下对应的几个方法:
    下面3个方法是Picasso这个类的:

    • cancelTag(Object tag) 取消设置了给定tag的所有请求
    • pauseTag(Object tag) 暂停设置了给定tag 的所有请求
    • resumeTag(Object tag) resume 被暂停的给定tag的所有请求

    还有一个方法是RequestCreator的:

    • tag(Object tag) 为请求设置tag

    几个方法的意思也很明确,就是我们可以暂停、resume、和取消请求,可以用在哪些场景呢?

    场景一: 比如一个照片流列表,当我们快速滑动列表浏览照片的时候,后台会一直发起请求加载照片的,这可能会导致卡顿,那么我们就可以为每个请求设置一个相同的Tag,在快速滑动的时候,调用pauseTag暂停请求,当滑动停止的时候,调用resumeTag恢复请求,这样的体验是不是就会更好一些呢。

    Adapter中添加如下代码:

    Picasso.with(this).load(mData.get(position))
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .tag("PhotoTag")
                    .into(holder.mImageView);
    

    Activity中为RecyclerView添加滑动监听:

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    final Picasso picasso = Picasso.with(MainActivity.this);
    
                    if (newState == SCROLL_STATE_IDLE) {
                        picasso.resumeTag("PhotoTag");
                    } else {
                        picasso.pauseTag("PhotoTag");
                    }
                }
            });
    

    场景二: 比如一个照片流列表界面,在弱网环境下,加载很慢,退出这个界面时可能会有很多请求没有完成,这个时候我们就可 以通过tag 来取消请求了。

     @Override
        protected void onDestroy() {
            super.onDestroy();
            Picasso.with(this).cancelTag("PhotoTag");
        }
    

    8. 同步/异步加载图片

    Picasso 加载图片也有同步/异步两种方式
    **1,get() 同步 **
    很简单,同步加载使用get() 方法,返回一个Bitmap 对象,代码如下:

     try {
               Bitmap bitmap =  Picasso.with(this).load(URL).get();
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    注意:使用同步方式加载,不能放在主线程来做。

    2,异步的方式加载图片,fetch()
    一般直接加载图片通过into显示到ImageView 是异步的方式,除此之外,还提供了2个异步的方法:

    • fetch() 异步方式加载图片
    • fetch(Callback callback) 异步方式加载图片并给一个回调接口。
      Picasso.with(this).load(URL).fetch(new Callback() {
                @Override
                public void onSuccess() {
                    //加载成功
                }
    
                @Override
                public void onError() {
                  //加载失败
                }
            });
    

    这里就要吐槽一下接口设计了,回调并没有返回Bitmap, 不知道作者是怎么考虑的,只是一个通知效果,知道请求失败还是成功。
    **fetch 方法异步加载图片并没有返回Bitmap,这个方法在请求成功之后,将结果存到了缓存,包括磁盘和内存缓存。所以使用这种方式加载图片适用于这种场景:知道稍后会加载图片,使用fetch 先加载缓存,起到一个预加载的效果。 **

    9. 缓存(Disk 和 Memory)

    Picasso 有内存缓存(Memory)和磁盘缓存( Disk), 首先来看一下源码中对于缓存的介绍:

    • LRU memory cache of 15% the available application RAM
    • Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only
      available on API 14+ <em>or</em> if you are using a standalone library that provides a disk cache on all API levels like OkHttp)
    • Three download threads for disk and network access.

    可以看出,内存缓存是使用的LRU 策略的缓存实现,它的大小是内存大小的15%,可以自定义它的大小,最后在扩展那一章节再讲,磁盘缓存是磁盘容量的2%但是不超过50M,不少于5M。处理一个请求的时候,按照这个顺讯检查:memory->disk->network 。先检查有木有内存缓存,如果命中,直接返回结果,否则检查磁盘缓存,命中则返回结果,没有命中则从网上获取。

    默认情况下,Picasso 内存缓存和磁盘缓存都开启了的,也就是加载图片的时候,内存和磁盘都缓存了,但是有些时候,我们并不需要缓存,比如说:加载一张大图片的时候,如果再内存中保存一份,很容易造成OOM,这时候我们只希望有磁盘缓存,而不希望缓存到内存,因此就需要我们设置缓存策略了。Picasso 提供了这样的方法。

    1,memoryPolicy 设置内存缓存策略
    就像上面所说的,有时候我们不希望有内存缓存,我们可以通过 memoryPolicy 来设置。MemoryPolicy是一个枚举,有两个值

    NO_CACHE:表示处理请求的时候跳过检查内存缓存
    **NO_STORE: ** 表示请求成功之后,不将最终的结果存到内存。

    示例代码如下:

    with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE) //静止内存缓存
                    .into(mBlurImage);
    

    2,networkPolicy 设置磁盘缓存策略
    和内存缓存一样,加载一张图片的时候,你也可以跳过磁盘缓存,和内存缓存策略的控制方式一样,磁盘缓存调用方法networkPolicy(NetworkPolicy policy, NetworkPolicy... additional) , NetworkPolicy是一个枚举类型,有三个值:

    NO_CACHE: 表示处理请求的时候跳过处理磁盘缓存
    ** NO_STORE:** 表示请求成功后,不将结果缓存到Disk,但是这个只对OkHttp有效。
    OFFLINE:
    这个就跟 上面两个不一样了,如果networkPolicy方法用的是这个参数,那么Picasso会强制这次请求从缓存中获取结果,不会发起网络请求,不管缓存中能否获取到结果。

    使用示例:

     with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)//跳过内存缓存
                    .networkPolicy(NetworkPolicy.NO_CACHE)//跳过磁盘缓存
                    .into(mBlurImage);
    

    强制从缓存获取:

     with(this).load(URL)
                    .placeholder(R.drawable.default_bg)
                    .error(R.drawable.error_iamge)
                    .networkPolicy(NetworkPolicy.OFFLINE)//强制从缓存获取结果
                    .into(mBlurImage);
    

    10. Debug 和日志

    1,缓存指示器

    上一节说了,Picasso 有内存缓存和磁盘缓存,先从内存获取,没有再去磁盘缓存获取,都有就从网络加载,网络加载是比较昂贵和耗时的。因此,作为一个开发者,我们往往需要加载的图片是从哪儿来的(内存、Disk还是网络),Picasso让我们很容易就实现了。只需要调用一个方法setIndicatorsEnabled(boolean)就可以了,它会在图片的左上角出现一个带色块的三角形标示,有3种颜色,绿色表示从内存加载、蓝色表示从磁盘加载、红色表示从网络加载。

     Picasso.with(this)
            .setIndicatorsEnabled(true);//显示指示器
    

    效果图:

    cache_indicator.png

    如上图所示,第一张图从网络获取,第二张从磁盘获取,第三张图从内存获取。

    看一下源码中定义指示器的颜色:

     /** Describes where the image was loaded from. */
      public enum LoadedFrom {
        MEMORY(Color.GREEN),
        DISK(Color.BLUE),
        NETWORK(Color.RED);
    
        final int debugColor;
    
        private LoadedFrom(int debugColor) {
          this.debugColor = debugColor;
        }
      }
    

    可以很清楚看出,对应三种颜色代表着图片的来源。

    2,日志
    上面的指示器能够很好的帮助我们看出图片的来源,但是有时候我们需要更详细的信息,Picasso,可以打印一些日志,比如一些关键方法的执行时间等等,我们只需要调用setLoggingEnabled(true)方法,然后App在加载图片的过程中,我们就可以从logcat 看到一些关键的日志信息。

       Picasso.with(this)
              .setLoggingEnabled(true);//开启日志打印
    

    11. Picasso 扩展

    到目前为止,Picasso的基本使用已经讲得差不多了,但是在实际项目中我们这可能还满足不了我们的需求,我们需要对它做一些自己的扩展,比如我们需要换缓存的位置、我们需要扩大缓存、自定义线程池、自定义下载器等等。这些都是可以的,接下来我们来看一下可以做哪些方面的扩展。

    1,用Builder 自己构造一个Picasso Instance
    我们来回顾一下前面是怎么用Picasso 加载图片的:

    Picasso.with(this)
           .load("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg")
           .into(mImageView);
    

    总共3步:
    1,用with方法获取一个Picasso 示例
    2,用load方法加载图片
    3,用into 放法显示图片

    首先Picasso是一个单例模式,我们每一次获取的示例都是默认提供给我们的实例。但是也可以不用它给的Instance,我们直接用builder来构造一个Picasso:

           Picasso.Builder builder = new Picasso.Builder(this);
            //构造一个Picasso
            Picasso picasso = builder.build();
            //加载图片
            picasso.load(URL)
                    .into(mImageView);
    

    这样我们就构造了一个局部的Picasso实例,当然了,我们直接用new 了一个builder,然后build()生成了一个Picasso。这跟默认的通过with方法获取的实例是一样的。那么现在我们就可以配置一些自定义的功能了。
    2, 配置自定义下载器 downLoader
    如果我们不想用默认提供的Downloader,那么我们可以自定义一个下载器然后配置进去。举个例子:

    (1) 先自定义一个Downloader(只是举个例子,并没有实现):

    /**
     * Created by zhouwei on 17/2/26.
     */
    
    public class CustomDownloader implements Downloader {
    
        @Override
        public Response load(Uri uri, int networkPolicy) throws IOException {
            return null;
        }
    
        @Override
        public void shutdown() {
    
        }
    }
    

    (2) 然后通过builder 配置:

            //配置下载器
            builder.downloader(new CustomDownloader());
            //构造一个Picasso
            Picasso picasso = builder.build();
    

    这样配置后,我们用build()生成的Picasso 实例来加载图片就会使用自定义的下载器来下载图片了。

    ** 3, 配置缓存**
    前面说过,内存缓存是用的LRU Cahce ,大小是手机内存的15% ,如果你想缓存大小更大一点或者更小一点,可以自定义,然后配置。

            //配置缓存
            LruCache cache = new LruCache(5*1024*1024);// 设置缓存大小
            builder.memoryCache(cache);
    

    上面只是一个简单的举例,当然了你可以自定义,也可以使用LRUCache,改变大小,改变存储路径等等。

    提示: 很遗憾,好像没有提供改变磁盘缓存的接口,那就只能用默认的了。

    4, 配置线程池
    Picasso 默认的线程池的核心线程数为3,如果你觉得不够用的话,可以配置自己需要的线程池,举个列子:

            //配置线程池
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            builder.executor(executorService);
    

    **5, 配置全局的 Picasso Instance **
    上面说的这些自定义配置项目都是应用在一个局部的Picasso instance 上的,我们不可能每一次使用都要重新配置一下,这样就太麻烦了。我们希望我们的这些自定义配置能在整个项目都应用上,并且只配置一次。其实Picasso 给我们提供了这样的方法。可以调用setSingletonInstance(Picasso picasso)就可以了,看一下这个方法的源码:

     /**
       * Set the global instance returned from {@link #with}.
       * <p>
       * This method must be called before any calls to {@link #with} and may only be called once.
       */
      public static void setSingletonInstance(Picasso picasso) {
        synchronized (Picasso.class) {
          if (singleton != null) {
            throw new IllegalStateException("Singleton instance already exists.");
          }
          singleton = picasso;
        }
      }
    

    设置一个通过with方法返回的全局instance。我们只希望配置一次,所以,我们应该在Application 的onCreate方法中做全局配置就可以了。app一启动就配置好,然后直接和前面的使用方法一样,调用with方法获取Picasso instance 加载图片就OK了。

    因此在Application 中添加如下代码:

     @Override
        public void onCreate() {
            super.onCreate();
            
            // 配置全局的Picasso instance 
            
            Picasso.Builder builder = new Picasso.Builder(this);
            //配置下载器
            builder.downloader(new CustomDownloader());
            //配置缓存
            LruCache cache = new LruCache(5*1024*1024);// 设置缓存大小
            builder.memoryCache(cache);
            //配置线程池
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            builder.executor(executorService);
    
            //构造一个Picasso
            Picasso picasso = builder.build();
            // 设置全局单列instance 
            Picasso.setSingletonInstance(picasso);
            
            
        }
    

    然后应用这些自定义配置加载图片

    Picasso.with(this).load("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg").into(mImageView);
    

    用法和以前的一样,但是我们已经将我们的自定义配置应用上了。

    结尾

    以上就是对Picasso 用法的全部总结,如有什么问题,欢迎留言指正。Picasso真的是一个强大的图片加载缓存库,API 简单好用,而且是链式调用的(这点我特别喜欢)。官方文档写的比较简单,很多用法都要看源码和注释才知道。希望本文能给才开始使用Picasso 的同学一点帮助。

    参考

    Picasso官网
    Picasso — Getting Started



    作者:依然范特稀西
    链接:https://www.jianshu.com/p/c68a3b9ca07a
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • Picasso(一基本使用)

    2018-06-24 21:06:24
    Picasso(一基本使用) 首先添加依赖(和我之前分析的EventBus一样,都是从一些重要的方法入手,获取不同的版本有所区别,但是相近的版本都是有联系的) implementation 'com.squareup.picasso:picasso:2.71828' ...

    Picasso(一基本使用)

    首先添加依赖(和我之前分析的EventBus一样,都是从一些重要的方法入手,获取不同的版本有所区别,但是相近的版本都是有联系的)

    implementation 'com.squareup.picasso:picasso:2.71828'
    

    下面是我们经常使用的模板代码

    Picasso.get()// 通过单例获取对象
                // 加载的网络URL地址,获取本地的资源路径也是可以的
                .load("url")
                // 图片的转换(个人对图片的处理)
                .transform(transformation)
                // 设置图片占位(当要加载的图片还没有显示的时候出现)
                .placeholder(R.drawable.ic_launcher_background)
                // 图片加载失败的时候出现
                .error(R.drawable.ic_launcher_foreground)
                // 设置图片的尺寸(此时时像素,当然也可以设置dp,用dimen即可)
                .resize(400, 600)
                // 设置图片的显示,防止图片出现拉伸,变形等
                .centerCrop()
                // 可以做动画
                .rotate(260)
                // 设置优先级
                .priority(Picasso.Priority.HIGH)
                // 设置标签给请求
                .tag("tag")
                // 设置内存缓存策略(默认时开启的)
                .memoryPolicy(MemoryPolicy.NO_CACHE)
                // 设置磁盘缓存策略(同样默认开启)
                .networkPolicy(NetworkPolicy.NO_CACHE)
                // 显示图片,此时的这个callBack回调接口可以实现也可以不实现
                .into(imageView, new Callback() {
                    @Override
                    public void onSuccess() {
                        System.out.println("请求成功");
                    }
    
                    @Override
                    public void onError(Exception e) {
                        System.out.println("请求失败");
                    }
                });
    

    对于图片转换,下面有一个例子(裁剪出一个圆形)

    private Transformation transformation = new Transformation() {
        @Override
        public Bitmap transform(Bitmap source) {
            // 获取源图片的宽高
            int width = source.getWidth();
            int height = source.getHeight();
            // 创建副本并绘制
            Bitmap copyBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(copyBitmap);
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(source, 0, 0, paint);
            return copyBitmap;
        }
    
        @Override
        public String key() {
            return "circle";
        }
    };
    

    上面的一个链式调用有一个令人疑惑的点就是,为什么要设置tag,就是为请求打上一个标签。因为打上了标签,我们就可以找到这个请求,并对其做一些操作,比如,暂停当前请求等。说到这,我们可能想到的就是ListView,在ListView如果使用Picasso,如果一时间加载大量的图片,难免会造成UI卡顿。就有下面的操作

    listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                Picasso picasso = Picasso.get();
                // 可以在滑动停止的时候加载图片,防止UI卡顿
                if (scrollState == SCROLL_STATE_IDLE) {
                    picasso.resumeTag("tag");
                } else {
                    picasso.pauseTag("tag");
                }
            }
    
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
            }
        });
    

    至于,其他的属性设置就比较好理解,就打算从获取单例(get()),load,into这几个核心方法来简单说明以下Picasso的一些原理。

    小结:

    看到这个章节,我们需要知道和了解的就是Picasso的基本使用,设置一些我们常用的属性,以及这些属性代表的意思即可。

    总结

    在这个方法中,我们主要是完成一些初始化和配置工作,比如Picasso的创建,信息的获取,以及Dispatcher的创建。知道其中重要的核心类,线程的调度的重要handler就可以了。

    展开全文
  • Picasso详解

    2020-02-25 14:32:03
    附:Data Binding Library详解:Android 依赖注入库之Data Binding Library(MVVM设计模式)详解

     

    1.简介

    picasso是Square公司开源的一个Android图形缓存库。可以实现图片下载和缓存功能。

     

    主要有以下一些特性:

     

       1.在adapter中回收和取消当前的下载。 

     

       2.使用最少的内存完成复杂的图形转换操作。

       

       3.自动的内存和硬盘缓存。

     

       4.图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作。

     

       5.加载载网络或本地资源。

     

     

    2.Picasso框架设计图

     

    如上图,整个库分为 Dispatcher,RequestHandler 及 Downloader,PicassoDrawable 等模块。 

    Dispatcher 负责分发和处理 Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。

    简单的讲就是 Picasso 收到加载及显示图片的任务,创建 Request 并将它交给 Dispatcher,Dispatcher 分发任务到具体 RequestHandler,任务通过 MemoryCache 及 Handler(数据获取接口) 获取图片,图片获取成功后通过 PicassoDrawable 显示到 Target 中。

    需要注意的是上面 Data 的 File system 部分,Picasso 没有自定义本地缓存的接口,默认使用 http 的本地缓存,API 9 以上使用 okhttp,以下使用 Urlconnection,所以如果需要自定义本地缓存就需要重定义 Downloader。

     

     

     

     

    3.Picasso框架特点

     

       1.自带统计监控功能。支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。

       2.支持优先级处理。每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于Icon时就很适用。

       3.支持延迟到图片尺寸计算完成加载。

       4.支持飞行模式、并发线程数根据网络类型而变。手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。

       5.“无”本地缓存。无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及Expired 控制图片的过期时间。

     

     

     

     

     

    4.代码说明

     

    4.1.AndroidStudio配置

    implementation 'com.squareup.picasso:picasso:2.71828'

     

     

     

     

    4.2.基本使用

     imageview=findViewById(R.id.activity_picass_imageview);
     String url="XXX";
     Picasso.get()
            .load(url)
            .into(imageview);

    .get():获取Picasso对象。

    .load(url):下载url对应的图片。

    .into(imageview):显示ImageView。

     

     

     

    4.3.加载其他路径下的图片

     

    4.3.1.加载本地图片 比如sd卡中的图片

     imageview=findViewById(R.id.activity_picass_imageview);
     File file = new File(Environment.getExternalStorageDirectory() + "/icon.png");
     Picasso.get()
            .load(file)
            .into(imageview);

     

    4.3.2.加载应用资源 比如mipmap下的图片

     imageview=findViewById(R.id.activity_picass_imageview);
     int resource = R.mipmap.za;
     Picasso.get()
            .load(resource)
            .into(imageview);

     

    4.3.3.加载应用资源 比如asset下的图片

     imageview=findViewById(R.id.activity_picass_imageview);
     String prefixurl="file:///android_asset/";
     Picasso.get()
            .load(prefixurl+"tab_my_pressed.png")
            .into(imageview);

     

    4.3.4.加载Uri对象 比如获取的相机相册图片

     

    imageview=findViewById(R.id.activity_picass_imageview);
    Uri imageUri =XXX;
    Glide.with(this)
          .load(imageUri)
          .into(imageview);

     

    附:load重载方法

     

     

     

     

     

    4.4.添加占位符

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="XXX";
    Picasso.get()
           .load(url)
           .placeholder(R.drawable.ic_launcher_background)
           .error(R.drawable.ic_launcher_background)
           .into(imageview);

     

     

    .placeholder(R.drawable.ic_launcher_background):图片加载中显示设置图片。

     

     

    .error(R.drawable.ic_launcher_background):图片下载失败显示设置图片。

     

     

     

    4.5.裁剪图片尺寸

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="XXX";
    Picasso.get()
           .load(url)
           .resize(20,20)
           .into(imageview);

    .resize(20,20):将图片剪切成20*20大小的图片(单位px)。

    同时,resize方法经常和centerCrop(),centerInside()等方法一起使用。但是后两者不可单独使用。

     

    附1:官方定义剪切

    英:Transform images to better fit into layouts and to reduce memory size。

    中:转换图像以更好地适应布局和减少内存大小。

     

    附2:计算出最佳的大小及最佳的图片质量来进行图片展示 (  减少内存 )

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="http://i.imgur.com/DvpvklR.png";
    Picasso.get()
           .load(url)
           .fit()
           .into(imageview);

    即.fit()方法。

     

     

     

     

     

    4.6.加载圆形图片

     

    4.6.1.自定义Transformation实现类

    public class CircleTransform implements Transformation {
    
    
        @Override
        public Bitmap transform(Bitmap source) {
    
            int size = Math.min(source.getWidth(), source.getHeight());
    
            int x = (source.getWidth() - size) / 2;
            int y = (source.getHeight() - size) / 2;
    
            Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
            if (squaredBitmap != source) {
                source.recycle();
            }
    
            Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
    
            Canvas canvas = new Canvas(bitmap);
            Paint paint = new Paint();
            BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
            paint.setShader(shader);
            paint.setAntiAlias(true);
            float r = size / 2f;//半径为图片宽高取最小的一个的一半
            canvas.drawCircle(r, r, r, paint);
            squaredBitmap.recycle();
            return bitmap;
        }
    
        @Override
        public String key() {
            return "circle";
        }
    
    }
    

     

    注:也可自定义半径

     

    float r = radius;
    private Context mContext;
    private int radius; // 圆形图片半径
    
    public CircleTransform(Context context) {
       mContext = context;
       radius = ActivityUtils.dip2px(mContext, 10);
    }

     

    4.6.2.使用

     

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="XXX";
    Picasso.get()
           .load(url)
           .transform(new CircleTransform())
           .into(imageview);

    使用.transform(new CircleTransform())方法,将自定义的Transformation实现类导入即可。

     

     

     

    4.7.加载圆角图片

     

    4.7.1.自定义Transformation实现类

    public class RoundTransform implements Transformation {
    
        private Context mContext;
    
        public RoundTransform(Context context) {
            mContext = context;
        }
    
        @Override
        public Bitmap transform(Bitmap source) {
            int widthLight = source.getWidth();
            int heightLight = source.getHeight();
            int radius = ActivityUtils.dip2px(mContext, 10); // 圆角半径
            Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(output);
            Paint paintColor = new Paint();
            paintColor.setFlags(Paint.ANTI_ALIAS_FLAG);
            RectF rectF = new RectF(new Rect(0, 0, widthLight, heightLight));
            canvas.drawRoundRect(rectF, radius, radius, paintColor);
            Paint paintImage = new Paint();
            paintImage.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
            canvas.drawBitmap(source, 0, 0, paintImage);
            source.recycle();
            return output;
        }
    
        @Override
        public String key() {
            return "roundcorner";
        }
    
    }
    

     

    4.7.2.使用

     

     imageview=findViewById(R.id.activity_picass_imageview);
     String url="XXX";
     Picasso.get()
            .load(url)
            .transform(new RoundTransform(PicassoActivity.this))
            .into(imageview);

    使用.transform(new RoundTransform(PicassoActivity.this))方法,将自定义的Transformation实现类导入即可。

     

     

     

     

    附:transform方法

     

    注意:设置圆角图片时,如果ImageView设置如下类型

    android:scaleType="centerCrop"

    则圆角没有效果。

     

     

     

     

     

    4.8.加载图片优先级

    因为Picasso是异步加载,所以多个图片时那个图片先加载出来是不一定的。如果用户想自定义图片加载的先后顺序。Picasso支持设置优先级,分为HIGH, NORMAL, 和 LOW,所有的加载默认优先级为NORMAL。

     

    代码:

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="http://i.imgur.com/DvpvklR.png";
    Picasso.get()
           .load(url)
           .priority(Picasso.Priority.HIGH)
           .into(imageview);

    priority(Picasso.Priority.HIGH):高

    priority(Picasso.Priority.NORMAL):正常(默认)

    priority(Picasso.Priority.LOW):低

     

     

     

    4.9.旋转图片

     imageview=findViewById(R.id.activity_picass_imageview);
     String url="http://i.imgur.com/DvpvklR.png";
     Picasso.get()
            .load(url)
            .rotate(90)
            .into(imageview);

    .rotate(90):顺时针旋转90°。(默认圆心(0,0))。

    .rotate(float degrees, float pivotX, float pivotY):自定义圆心。

     

     

     

    4.10.Tag设置

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="http://i.imgur.com/DvpvklR.png";
    Picasso.get()
           .load(url)
           .tag(1)
           .into(imageview);

    .tag(1)方法:

     

     

     

     

    4.11.内存问题

    无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。

     

    imageview=findViewById(R.id.activity_picass_imageview);
    String url="http://i.imgur.com/DvpvklR.png";
    Picasso.get()
           .load(url)
           .memoryPolicy(NO_CACHE, NO_STORE)
           .into(imageview);

     

     

     

     

    4.12.列表使用

    Picasso实现了图片的异步加载,并解决了Android中加载图片时常见的一些问题,它有以下特点:
     

    在Adapter中取消了不在视图范围内的ImageView的资源加载,因为可能会产生图片错位。

    使用复杂的图片转换技术降低内存的使用。

    自带内存和硬盘的二级缓存机制。

     

    代码:

     

    Adapter:

    public class PicassoAdapter extends BaseAdapter {
    
        private Context context;
        private List<Student> list;
    
        public PicassoAdapter(Context context,List<Student> list) {
            this.context = context;
            this.list = list;
        }
    
        @Override
        public int getCount() {
            return list.size();
        }
    
        @Override
        public Object getItem(int position) {
            return list.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder viewHolder;
            if (convertView == null) {
                convertView = View.inflate(context, R.layout.glidelistview_item, null);
                viewHolder = new ViewHolder();
                viewHolder.circleImageView = convertView.findViewById(R.id.glidelistview_circleimageview);
                viewHolder.textView1 = convertView.findViewById(R.id.glidelistview_item_textview1);
                viewHolder.textView2 = convertView.findViewById(R.id.glidelistview_item_textview2);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            viewHolder.textView1.setText(list.get(position).getName() + "【" + (position + 1) + "】");
            viewHolder.textView2.setText(list.get(position).getAge());
            String ava = list.get(position).getAva();
            final int picassoTag=position;
            if(!BooleanUtils.isEmpty(ava)){//头像不为空
                //文字显示颜色
                viewHolder.textView1.setTextColor(context.getResources().getColor(R.color.colorPrimary));
                viewHolder.textView2.setTextColor(context.getResources().getColor(R.color.colorPrimary));
                //Picasso加载图片
                Picasso.get()
                        .load(ava)
                        .placeholder(R.mipmap.patient_ava)
                        .error(R.mipmap.patient_ava)
                        .tag(picassoTag)
                        .transform(new CircleTransform())
                        .into(viewHolder.circleImageView);
            }else{//头像为空
                //文字显示颜色
                viewHolder.textView1.setTextColor(context.getResources().getColor(R.color.colorAccent));
                viewHolder.textView2.setTextColor(context.getResources().getColor(R.color.colorAccent));
                //默认图片
                viewHolder.circleImageView.setBackgroundResource(R.mipmap.patient_ava);
            }
            return convertView;
        }
    
        public static class ViewHolder {
            private ImageView circleImageView;
            private TextView textView1;
            private TextView textView2;
        }
    
    }
    

     

     

     

    Activity:

    public class PicassoListActivity extends AppCompatActivity {
    
        private ListView listview;
        private PicassoAdapter adapter;
        private List<Student> list;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_glide);
            onWindowbar();
            initData();
        }
    
        /**
         * 赋值
         */
    
        private void initData() {
            listview=findViewById(R.id.activity_listview_listview);
            list=new ArrayList<>();
            Student student1=new Student();
            student1.setName("詹姆斯");
            student1.setAva("XXX");
            student1.setAge(student1.getName()+":很懒什么也没留下!");
    
            Student student2=new Student();
            student2.setName("韦德");
            student2.setAva("XXX");
            student2.setAge(student2.getName()+":很懒什么也没留下!");
    
            Student student3=new Student();
            student3.setName("山东鲁能");
            student3.setAva("XXX");
            student3.setAge(student3.getName()+":很懒什么也没留下!");
    
            Student student4=new Student();
            student4.setName("保罗");
            student4.setAva("XXX");
            student4.setAge(student4.getName()+":很懒什么也没留下!");
    
            Student student5=new Student();
            student5.setName("安东尼");
            student5.setAva("XXX");
            student5.setAge(student5.getName()+":很懒什么也没留下!");
    
            Student student6=new Student();
            student6.setName("蒿俊闵");
            student6.setAva("XXX");
            student6.setAge(student6.getName()+":很懒什么也没留下!");
    
            Student student7=new Student();
            student7.setName("李霄鹏");
            student7.setAva("");
            student7.setAge(student7.getName()+":没有上传头像!");
    
            for(int i=0;i<30;i++){
                list.add(student1);
                list.add(student2);
                list.add(student3);
                list.add(student4);
                list.add(student5);
                list.add(student6);
                list.add(student7);
            }
    
            adapter=new PicassoAdapter(PicassoListActivity.this,list);
            listview.setAdapter(adapter);
        }
    
        /**
         * onKeyDown方法
         */
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                finish();
                overridePendingTransition(0, R.anim.activity_right_open);
                return false;
            }
            return super.onKeyDown(keyCode, event);
        }
    
    }
    

     

     

     

     

     

     

     

     

    附1:框架GitHub链接

    https://github.com/square/picasso

     

     

    附2:

    一个GitHub上的各种形状的ImageView链接:https://github.com/vinc3m1/RoundedImageView

     

     

    附3:几种图片框架对比图

     

     

    展开全文
  • Picasso

    2019-04-13 09:54:47
    简介 xxx 依赖 implementation 'com.squareup.picasso:picasso:2.4.0' ...Picasso.with(mContext) .load(imageFile) .placeholder(R.drawable.default_error) .tag(ImageSelectFragment.TAG) ...
  • Picasso使用简介及分析

    2017-07-13 14:55:49
    Picasso是Square公司出品的一款非常优秀的开源图片加载库,是目前Android开发中超级流行的图片加载库之一, Picasso还能自动帮我们做以下事情: 处理Adapter 中ImageView的回收和取消下载。使用最小的内存 ...
  • picasso使用方法与原理分析(一)

    千次阅读 2018-08-31 13:36:37
    最初的imageloader(目前已经不再维护),目前的picasso, glide。picasso和glide在加载展示图片这个模块其实相似度蛮高,谷歌也在很多项目中使用了glide。但这并不是说glide就在任何项目都是最适合的,我们必须在...
  • Picasso简介<一>

    2017-06-08 15:07:55
    一,简介: 1简意:picasso是Square公司开源的一个Android图形缓存库,...(1)在adapter中需要取消已经不在视野范围的ImageView图片资源的加载,否则会导致图片错位,Picasso已经解决了这个问题 (2)复杂的图片进行
  • picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓存功能。仅仅只需要一行代码就能完全实现图片的异步加载: Picasso.with(context).load(...
  • 安卓图片处理Picasso的解析使用

    千次阅读 2016-02-01 09:41:27
    前言今天来学习使用一下毕加索Picasso),Picasso是个什么鬼?有什么作用? Picasso其实是Android系统的图片下载和缓存类库,是Square开源的一个用于Android系统下载和缓存图片的项目。下面我们就来讲讲在...
  • 图片加载框架Picasso解析

    千次阅读 2015-04-28 17:38:32
    picasso是Square公司开源的一个Android图形缓存库 主要有以下一些特性: 在adapter中回收和取消当前的下载; 使用最少的内存完成复杂的图形转换操作; 自动的内存和硬盘缓存; 图形转换操作,如变换大小,旋转等,...
  • Android Picasso 原理解析

    2019-07-31 11:04:22
    Android Picasso 原理解析一.提供的功能特性二.简单使用三.源码结构1.整体类图2.内存缓存3.磁盘缓存4.Picasso对象5.RequestCreator对象6.Action对象7.Dispatcher对象8.RequestHandler对象9.BitmapHunter对象10.图片...
  • Android Picasso教程

    2020-07-20 22:36:05
    Android Picasso is a powerful image downloading and caching library. In this tutorial, we’ll be discussing and implementing Picasso library in our android application. Android Picasso是一个功能强大的...
  • Picasso源码原理分析

    千次阅读 2017-07-30 21:27:57
    作者:谭东 QQ:852041173 Picasso是美国SQUARE移动支付公司开源的图片缓存加载库。可以实现图片下载和缓存功能,效率和性能都很不错。 Square公司官方博客:...Square公司Picasso
  • Picasso源码的简单解析(一)

    千次阅读 2016-08-12 23:49:38
    前段时间简单的分析了一下ImageLoader的源码,所以就想看看使用...闲言少叙,开始Picasso分析之旅吧。 其实既然是缓存,肯定核心也就是那么几点:memory cache,disk cache等等,总体上来说只是不同的库对它们处理的方
  • Picasso高逼格使用技巧

    千次阅读 2016-07-03 13:36:16
    1 设置Picasso的缓存路径---默认Picasso的缓存都是在内置存储的cache路径下,如果下载较大图片则会导致下载不成功,但是系统也没有报错 坑。。。 2 Picasso下载图片时,自带下载进度提示,当下载完成时显示图片,如...
  • 现在流行的图片加载缓存类库有glide、picasso、ImageLoader 现在有个需求,就是app中要求点击图片放大功能,为了简便我们准备调用系统图片查看器,但是系统图片查看器是查看本地文件的,所以会需要把图片下载到...
  • Picasso图片加载框架的使用 Android 中有几个比较有名的图片加载框架,Universal ImageLoader、Picasso、Glide和Fresco。它们各有优点,以前一直用的是ImageLoader 做项目中的图片加载,由于作者宣布ImageLoader ...
  • (picasso?fresco?glide?bala.bala.) 如何杜绝OOM(565?Activity空壳化?largeHeep?nodpi?独立进程?) 更深次的优化,本地cache高斯模糊、旋转、缩放后的结果,归类?管理?预加载?用base64直接带了一些缩略图在普通的REST...
1 2 3 4 5 ... 20
收藏数 15,170
精华内容 6,068
关键字:

picasso