Android图片加载利器之Picasso源码解析

看到了这里,相信大家对Picasso的使用已经比较熟悉了,本篇博客中将从基本的用法着手,逐步的深入了解其设计原理。

Picasso的代码量在众多的开源框架中算得上非常少的一个了,一共只有35个class文件,但是麻雀虽小,五脏俱全。好了下面跟随我的脚步,出发了。

基本用法

Picasso.with(this).load(imageUrl).into(imageView);

with(this)方法

 public static Picasso with(Context context) {
  if (singleton == null) {
   synchronized (Picasso.class) {
    if (singleton == null) {
     singleton = new Builder(context).build();
    }
   }
  }
  return singleton;
 }

非常经典的单例模式,双重校验锁

在这多说一句,关于单例模式的实现方式一共有五种,分别是懒汉式,饿汉式,双重校验锁,内部静态类和枚举,其中使用的最多的就是双重校验锁和内部静态类的两种实现方式,主要优点是程序执行效率高,适应多线程操作。
接下来看下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;

  /**
   * 根据context获取Application的context
   * 此方式主要是为了避免context和单例模式的生命周期不同而造成内存泄漏的问题
   */
  public Builder(Context context) {
   ...
   this.context = context.getApplicationContext();
  }

   /** 设置图片的像素格式,默认为ARGB_8888 */
  public Builder defaultBitmapConfig(Bitmap.Config bitmapConfig) {
   ...
   this.defaultBitmapConfig = bitmapConfig;
   return this;
  }

  /** 自定义下载器,默认OkHttp,具体的实现类是OkHttpDownloader */
  public Builder downloader(Downloader downloader) {
   ...
   this.downloader = downloader;
   return this;
  }

  /** 自定义线程池,默认的实现是PicassoExecutorService */
  public Builder executor(ExecutorService executorService) {
   ...
   this.service = executorService;
   return this;
  }

  /** 自定义缓存策略,默认实现为LruCache */
  public Builder memoryCache(Cache memoryCache) {
   ...
   this.cache = memoryCache;
   return this;
  }

  /** 图片加载失败的一个回调事件 */
  public Builder listener(Listener listener) {
   ...
   this.listener = listener;
   return this;
  }

  /** 请求的转换,在request被提交之前进行转换 */
  public Builder requestTransformer(RequestTransformer transformer) {
   ...
   this.transformer = transformer;
   return this;
  }

  /** 自定义加载图片的来源 */
  public Builder addRequestHandler(RequestHandler requestHandler) {
   ...
   requestHandlers.add(requestHandler);
   return this;
  }

  //省略调试相关方法

  /** Create the {@link Picasso} instance. */
  public Picasso build() {
   Context context = this.context;

   if (downloader == null) {
    downloader = Utils.createDefaultDownloader(context);
   }
   if (cache == null) {
    cache = new LruCache(context);
   }
   if (service == null) {
    service = new PicassoExecutorService();
   }
   if (transformer == null) {
    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);
  }
 }

又是一个非常经典的设计模式,建造者模式或者被称为Buider模式,最大的特点就是链式调用,使调用者的代码逻辑简洁,同时扩展性非常好。

我们阅读优秀框架源码的好处就在于学习里面的设计思想,最终能够使用到自己的项目中

with方法分析完了,我们得到了一个Picasso的对象

load(imageUrl)方法

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

load重载方法比较多,但是都比较简单就是创建了一个RequestCreator对象

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

又是一个建造者模式,得到了一个Request.Builder对象赋值给了data变量。

into(imageView)方法

这个方法相对复杂一些,注释尽量描述的清楚一些,看代码

 public void into(ImageView target, Callback callback) {
  long started = System.nanoTime();
  // 只能在主线程中调用
  checkMain();

  // hasImage()的判断逻辑是设置了uri或者resourceId返回true
  // 如果都未设置则判断是否设置了placeholder,也就是默认显示的图片
  if (!data.hasImage()) {
   picasso.cancelRequest(target);
   if (setPlaceholder) {
    setPlaceholder(target, getPlaceholderDrawable());
   }
   return;
  }

  // 当设置了fit()时deferred值为true,也就是完全填充
  if (deferred) {
   int width = target.getWidth();
   int height = target.getHeight();
   if (width == 0 || height == 0) {
    if (setPlaceholder) {
     setPlaceholder(target, getPlaceholderDrawable());
    }
    picasso.defer(target, new DeferredRequestCreator(this, target, callback));
    return;
   }
   // 根据target也就是ImageView的大小下载图片
   data.resize(width, height);
  }
  // 见下方详解1
  Request request = createRequest(started);
  // 这个方法的作用就是根据上面的到的Request对象里面绑定的一些参数来生成一个字符串作为key值,
  // 逻辑比较清晰,主要包括stableKey(这个是用户自定义的key值,在第二篇文章中有介绍)、uri、旋转角度、大小、填充方式。
  String requestKey = createKey(request);
  // 根据用户的设置是否从缓存里取图片信息
  if (shouldReadFromMemoryCache(memoryPolicy)) {
   // 在LruCache中使用LinkedHashMap<String, Bitmap>来保存图片信息,key就是上面生成的requestKey
   // 在LruCache的get方法中返回Bitmap对象,并记录命中或者未命中。
   Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
   if (bitmap != null) {
    picasso.cancelRequest(target);
    setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
    // 这个callback是异步加载图片的一个回调,之前忘记介绍了,看来需要再补充一篇文章来介绍异步和同步请求
    if (callback != null) {
     callback.onSuccess();
    }
    return;
   }
  }
  // 如果有设置了默认显示的图片,则先将其显示出来
  if (setPlaceholder) {
   setPlaceholder(target, getPlaceholderDrawable());
  }

  // 又出来一个ImageViewAction,可以看到里面传递了前面准备好的全部数据,那么这个对象又是做什么的呢?
  // 在ImageViewAction代码中提供了三个方法complete、error、cancel,所以可以猜想这个是用作处理最后的下载结果的
  // 如果成功了就将其显示出来,如果失败则显示用户通过error方法设置的图片
  Action action =
    new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
      errorDrawable, requestKey, tag, callback, noFade);
  // 这里又回到了Picasso类中,见下方详解2
  picasso.enqueueAndSubmit(action);
 }

详解1 createRequest

 private Request createRequest(long started) {
  // 返回nextId的值并将其+1,有一个与之对应的方法是incrementAndGet,这个表示先+1再返回
  int id = nextId.getAndIncrement();

  // 这里面构造了一个Request对象,它是一个实体类用来存放我们请求图片的一些参数
  // 包括地址,大小,填充方式,旋转参数,优先级等等
  Request request = data.build();
  request.id = id;
  request.started = started;

  // 判断是否有进行request转化,在上一篇文章中介绍了转换的方法
  Request transformed = picasso.transformRequest(request);
  if (transformed != request) {
   transformed.id = id;
   transformed.started = started;
  }

  return transformed;
 }

详解2 enqueueAndSubmit

从名字可以看到是将action加入到了一个队列中,经过几次转换过程,从Picasso类中跑到了Dispatcher类中,这个我们在上面提到过,是一个调度器,下面我们进入Dispatcher中看看实现逻辑

dispatcher.dispatchSubmit(action);

再次经过几经周转,最终的实现代码如下

 void performSubmit(Action action, boolean dismissFailed) {
  // 首先根据tag判断是否已经下发了暂停下载的命令,pausedTags是WeakHashMap类型的集合
  if (pausedTags.contains(action.getTag())) {
   pausedActions.put(action.getTarget(), action);
   return;
  }
  // hunterMap是LinkedHashMap<String, BitmapHunter>()类型的对象,用来保存还未执行的下载请求
  BitmapHunter hunter = hunterMap.get(action.getKey());
  if (hunter != null) {
   // 如果新的请求的key值在LinkedHashMap中存在,则合并两次请求,并重新处理优先级
   hunter.attach(action);
   return;
  }

  // 这个方法主要用来判断该请求采用哪一种requestHandler,Picasso提供了7种,我们也可以自定义
  hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  // 将hunter添加到线程池中,hunter是Runnable的一个实现
  hunter.future = service.submit(hunter);
  hunterMap.put(action.getKey(), hunter);
  if (dismissFailed) {
   failedActions.remove(action.getTarget());
  }
 }

提交到线程池之后就等待线程池调度了,一旦有空闲线程则将会执行BitmapHunter的run方法

// 这里只保留了关键的代码,调用了hunt方法,得到了result对象,然后再通过dispatcher进行分发
 public void run() {
  result = hunt();
  if (result == null) {
    dispatcher.dispatchFailed(this);
  } else {
    dispatcher.dispatchComplete(this);
  }
 }
 Bitmap hunt() throws IOException {
  Bitmap bitmap = null;
  // 再次检查内存缓存,和之前的逻辑一样
  if (shouldReadFromMemoryCache(memoryPolicy)) {
   bitmap = cache.get(key);
   if (bitmap != null) {
    stats.dispatchCacheHit();
    loadedFrom = MEMORY;
    return bitmap;
   }
  }
  // networkPolicy这个值怎么计算的呢?我们先看retryCount是如何得到的
  // 在构造方法中this.retryCount = requestHandler.getRetryCount();
  // 那么来看getRetryCount()方法得到的值是否为0,代码中一共有七个类重载了RequestHandler
  // 在RequestHandler类中默认返回0,而只有NetworkRequestHandler重写了getRetryCount()方法,返回2
  // 因此就是说当不是从网络请求图片时data.networkPolicy = NetworkPolicy.OFFLINE.index
  data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
  // 七个类重载了RequestHandler并且都实现了自己的load方法
  // 这里面我们只看网络相关的NetworkRequestHandler,其余的感兴趣的童鞋可以自己看下代码
  // 我们先看下下面的关于 NetworkRequestHandler中load方法的代码,再回来继续分析
  RequestHandler.Result result = requestHandler.load(data, networkPolicy);
  if (result != null) {
   loadedFrom = result.getLoadedFrom();
   exifRotation = result.getExifOrientation();
   // 解析bitmap
   bitmap = result.getBitmap();
   if (bitmap == null) {
    InputStream is = result.getStream();
    try {
     bitmap = decodeStream(is, data);
    } finally {
     Utils.closeQuietly(is);
    }
   }
  }
  // 这一段主要是看用户是否设置图片的转换处理
  if (bitmap != null) {
   stats.dispatchBitmapDecoded(bitmap);
   if (data.needsTransformation() || exifRotation != 0) {
    synchronized (DECODE_LOCK) {
     if (data.needsMatrixTransform() || exifRotation != 0) {
      bitmap = transformResult(data, bitmap, exifRotation);、
     }
     if (data.hasCustomTransformations()) {
      bitmap = applyCustomTransformations(data.transformations, bitmap);
     }
    }
    if (bitmap != null) {
     stats.dispatchBitmapTransformed(bitmap);
    }
   }
  }
  return bitmap;
 }
/**
 * OkHttpDownloader中的load方法,返回了Result对象
 */
 public Result load(Request request, int networkPolicy) throws IOException {
  // 这里面如果我们自己没有自定义下载器,则执行的是OkHttpDownloader中的load方法,继续深入到load方法中一探究竟,代码在下方了,这里面得到的response是OkHttp给我们返回来的
  Response response = downloader.load(request.uri, request.networkPolicy);
  // 得到加载位置是SdCard还是网络
  Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
  // 下面分别获取了Bitmap和InputStream,同时返回了Result对象,我们返回到上面继续分析
  Bitmap bitmap = response.getBitmap();
  if (bitmap != null) {
   return new Result(bitmap, loadedFrom);
  }

  InputStream is = response.getInputStream();
  if (loadedFrom == NETWORK && response.getContentLength() > 0) {
   stats.dispatchDownloadFinished(response.getContentLength());
  }
  return new Result(is, loadedFrom);
 }
/**
 * 这个方法中主要使用了CacheControl来承载缓存策略,同时将Request对象传入了OkHttp中
 * 看到这里Picasso源码已经走到了尽头,如果想继续分析,只能查看OkHttp的代码了,目前我还没有通读过,
 * 所以我们将得到的结果向上继续看了,以后有时间我也会更新一些关于OkHttp的源码解析。
 * BUT 我们目前只看到了判断内存中是否有缓存,SDCard的缓存还没有判断呢?
 * 没错,关于SdCard的读取和写入都是有OkHttp来完成的,当然了我们也可以自定义下载器,
 * 在这里就能看出来Picasso和OkHttp果然是亲戚啊!连SdCard的缓存都帮忙实现了。
 */
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);
  }

  com.squareup.okhttp.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() != null;

  ResponseBody responseBody = response.body();
  return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
 }

走到了这里我们已经得到了结果,是一个result对象,然后再通过dispatcher进行分发,进入Dispatcher类中,最终执行的方法如下

 void performComplete(BitmapHunter hunter) {
  // 判断用户是否设置了写缓存,默认是需要写入内存的
  if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
   cache.set(hunter.getKey(), hunter.getResult());
  }
  // hunterMap我们在前面介绍过了,用来保存还未执行的下载请求,因此下载完成之后将其remove到
  hunterMap.remove(hunter.getKey());
  // 接着看batch的实现
  batch(hunter);
 }
 private void batch(BitmapHunter hunter) {
  // 将BitmapHunter对象加入到了batch变量中,batch是一个ArrayList类型的集合
  batch.add(hunter);
  // 到这里并没有直接将图片显示出来,而是填加到list中,发送了一个延迟消息,延迟200ms
  // 其实这是一个批处理,让本次事件尽快结束,不影响界面的其他操作
  // 下面我们跟进handler的HUNTER_DELAY_NEXT_BATCH语句中
  if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
   handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
  }
 }
 void performBatchComplete() {
  List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
  batch.clear();
  // 将batch里的数据复制了一份,又通过mainThreadHandler发送了一个HUNTER_BATCH_COMPLETE的消息
  // mainThreadHandler是怎么来的呢?原来是在Dispatcher的构造方法中传进来的,那么我们就要回头找找什么时候创建的Dispatcher对象
  // 原来是在Picasso的Builder类build的时候创建的,而Handler也就是在Picasso类中定义,代码如下
  mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
  logBatch(copy);
 }

几经周转,最终我们又回到了Picasso的类中

 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;
    }
   }
  }
 };

上面的代码比较好理解了,我们传进来的是由多个BitmapHunter对象组成的list,在这里做个遍历调用complete方法。这时候已经回到了主线成中,图片马上就要显示出来了

 void complete(BitmapHunter hunter) {
  Action single = hunter.getAction();
  List<Action> joined = hunter.getActions();

  boolean hasMultiple = joined != null && !joined.isEmpty();
  boolean shouldDeliver = single != null || hasMultiple;

  if (!shouldDeliver) {
   return;
  }

  Uri uri = hunter.getData().uri;
  Exception exception = hunter.getException();
  Bitmap result = hunter.getResult();
  LoadedFrom from = hunter.getLoadedFrom();
  // 这里面来说一下single和joined,还记不记得前面分析到Dispatcher类中的performSubmit方法时
  // 判断了hunterMap中如果有相同的key值则执行hunter.attach(action);
  // 因此single得到的action是hunterMap中没有相同的key值时的action
  // 而当hunterMap中存在未处理的key与新的请求的key值相同时则将action添加到了BitmapHunter类的actions对象中
  // 因此joined保存的就是与single中具有相同key值的数据,所以要分别处理
  if (single != null) {
   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 != null && exception != null) {
   listener.onImageLoadFailed(this, uri, exception);
  }
 }

接下来进入deliverAction方法中

 private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
  ...
  // 关键代码就这一句
  action.complete(result, from);
  ...
 }

此时进入到了ImageViewAction中的complete方法中,我们在上面提到过ImageViewAction类的作用,是用来处理最后的下载结果的,好激动啊!图片马上就显示出来了~~~

 @Override
 public void complete(Bitmap result, Picasso.LoadedFrom from) {
  ImageView target = this.target.get();
  Context context = picasso.context;
  boolean indicatorsEnabled = picasso.indicatorsEnabled;
  // 关键代码,进入PicassoDrawable的setBitmap方法中一探究竟
  PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

  if (callback != null) {
   callback.onSuccess();
  }
 }
 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();
  }
  // 这里面主要是对显示效果进行处理,最终得到了一个PicassoDrawable对象,继承了BitmapDrawable
  PicassoDrawable drawable =
    new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
  // 至此图片终于终于显示出来了~~~~~~
  target.setImageDrawable(drawable);
 }

写源码分析太苦了,我已经尽可能的描述的清楚一些,如果有哪块不太理解的,可以和我交流~~~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android图片加载利器之Picasso基本用法

    今天开始我们来学习一下Picasso,计划包括以下几方面的内容: 图片加载利器之Picasso进阶 图片加载利器之Picasso源码解析 目前市场上比较流行的图片加载框架主要有UniversalImageLoader,Picasso,Glide,Fresco. 下面简单介绍一下这几个框架: UniversalImageLoader:这个可以说是非常非常经典的一个了,相信每个app的开发人员都使用过,只可惜作者已经停止该项目的维护了,所以不太推荐使用. Picasso:是Square公司出品的图片

  • Android入门教程之Picasso框架

    一.简介: Picasso是Square公司开源的一个Android图形缓存库.可以实现图片下载和缓存功能. 二.Picasso的特性 Picasso是一个Android图片加载缓存框架,它具有如下特性: 1.支持任务优先级,会优先加载"优先级"较高的图片. 2.带有统计监控功能,可以统计缓存命中率,实时监控已使用的内存等等. 3.能够根据当前网络状态自动调整并发线程数. 4.支持图片的延迟加载. 5.本身不具有本地缓存,而是使用的OkHttp实现. Picasso除了使用上比较简单.

  • Android的Glide库加载图片的用法及其与Picasso的对比

    Glide Glide是一个高效.开源. Android设备上的媒体管理框架,它遵循BSD.MIT以及Apache 2.0协议发布.Glide具有获取.解码和展示视频剧照.图片.动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里.创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取.大小调整和展示. Glide 3.0版本以后加入了多项重要功能,同时还支持使用Gradle以及Maven进行构建.该版本包括很多值得关

  • Android图片加载利器之Picasso扩展功能

    这篇文章中我们来继续学习Picasso中还提供了哪些扩展功能,一个好的框架一定是扩展性强的,你需要的我刚好有. 下面看一下都提供了哪些扩展功能呢? Picasso内部采用了建造者模式,所以我们只看Builder内部类都提供了哪些方法即可 1 defaultBitmapConfig(Bitmap.Config bitmapConfig) 设置像素格式,Picasso的默认格式为ARGB_8888,我们知道采用这个值一个像素占用4个字节,可以使用RGB_565来替换,这样可以节省一半的内存开销,当然

  • Android Picasso使用高斯模糊处理的示例代码

    众所周知,Picasso是一个在Android开发中经常使用到的图片加载库,常规用法一般是这样: Picasso.with(context) .load(img_url) .config(Bitmap.Config.RGB_565) .into(imageView); 还有不使用缓存的用法: Picasso.with(context) .load(img_url) .config(Bitmap.Config.RGB_565) .memoryPolicy(MemoryPolicy.NO_CACHE

  • Android图片加载利器之Picasso源码解析

    看到了这里,相信大家对Picasso的使用已经比较熟悉了,本篇博客中将从基本的用法着手,逐步的深入了解其设计原理. Picasso的代码量在众多的开源框架中算得上非常少的一个了,一共只有35个class文件,但是麻雀虽小,五脏俱全.好了下面跟随我的脚步,出发了. 基本用法 Picasso.with(this).load(imageUrl).into(imageView); with(this)方法 public static Picasso with(Context context) { if

  • Android用于加载xml的LayoutInflater源码超详细分析

    1.在view的加载和绘制流程中:文章链接 我们知道,定义在layout.xml布局中的view是通过LayoutInflate加载并解析成Java中对应的View对象的.那么具体的解析过程是哪样的. 先看onCreate方法,如果我们的Activity是继承自AppCompactActivity.android是通过getDelegate返回的对象setContentView,这个mDelegate 是AppCompatDelegateImpl的实例. @Override protected

  • Android图片加载框架Coil的详细使用总结

    目录 简介 简单使用 高斯模糊 圆角 圆形 灰色变换 GrayscaleTransformation Gif 监听下载过程 取消下载 替换 okhttp 实例 自定义 Coil 源码分析 总结 简介 Coil 是一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片.特点如下: 更快: Coil 在性能上有很多优化,包括内存缓存和磁盘缓存,把缩略图存保存在内存中,循环利用 bitmap,自动暂停和取消图片网络请求等. 更轻量级: Coil 只有2000个方法(前提是你的 APP

  • 一起动手编写Android图片加载框架

    开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做出比较,来帮助我们量化这个框架的性能.通过开发这个框架,我们可以进一步深入了解Android中的Bitmap操作.LruCache.LruDiskCache,让我们以后与Bitmap打交道能够更加得心应手.若对Bitmap的大小计算及inSampleSize计算还不太熟悉,请参考这里:高效加载Bitmap.由于个人水平有限,叙述中必然存在

  • Android图片加载的缓存类

    本文为大家分享了Android图片加载的缓存类,供大家参考,具体内容如下 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReferenc

  • Android图片加载库Glide用法

    目录 Glide介绍 Android SDK 要求 使用前的准备 基本用法 在 ListView 和 RecyclerView 中的使用 占位符 选项 过渡动画 变换效果 使用示例 Glide介绍 Glide是一个快速高效的Android图片加载库,注重于平滑的滚动.Glide提供了易用的API,高性能.可扩展的图片解码管道,以及自动的资源池技术.Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快.更平滑. Android SDK 要求 Min Sdk Version - 使用

  • pytorch加载自己的数据集源码分享

    目录 一.标准的数据集流程梳理 数据来源 二.实现加载自己的数据集 1. 保存在txt文件中(生成训练集和测试集,其实这里的训练集以及测试集也都是用文本文件的形式保存下来的) 2. 在继承dataset类LoadData的三个函数里调用train.txt以及test.txt实现相关功能 三.源码 一.标准的数据集流程梳理 分为几个步骤数据准备以及加载数据库–>数据加载器的调用或者设计–>批量调用进行训练或者其他作用 数据来源 直接读取了x和y的数据变量,对比后面的就从把对应的路径写进了文本文件

  • Android音视频开发Media FrameWork框架源码解析

    目录 一.Media FrameWork背景 二.Media Framework“路线图” 2.1 代理端 2.2 服务端 2.2.1 Source 2.2.2 Decoder 2.2.3 Renderer 2.2.4 Foundation 2.3 OMX端 2.4 Kernel端 三.media播放的流程 四.Media FrameWork源码分析 一.Media FrameWork背景 Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视

随机推荐