Android Glide的基本使用方法 , 内存优化以及源码解析

使用Glide,首先就是要导入一个依赖包(在moudle的Build.gradle里面设置):

    compile 'com.github.bumptech.glide:glide:4.0.0-RC0'

如果需要图片戳圆,那就用4.0以上的Glide依赖包

    Glide.with(MainActivity.this).load("").into(mImg);

先说说with方法吧,with方法里面可以放,Context,Activity,FragmentActivity,Fragment甚至是View不同的类型,我们来看看源码:

with:

它是通过RequestManager拿到的with里面的属性

public static RequestManager with(FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}

RequestManager是实现的LifecycleListenner

public class RequestManager implements LifecycleListener {

而LifeCycleListenner是一个接口,接口里面有三个方法,也就是三个生命周期,从这里可以发现原来activity是和fragment的生命周期绑定的,

因为activity和fragment里面有相同的生命周期。这样就可以明白with后面可以放不同的上下文引用了。

public interface LifecycleListener {

  /**
   * Callback for when {@link android.app.Fragment#onStart()}} or {@link
   * android.app.Activity#onStart()} is called.
   */
  void onStart();

  /**
   * Callback for when {@link android.app.Fragment#onStop()}} or {@link
   * android.app.Activity#onStop()}} is called.
   */
  void onStop();

  /**
   * Callback for when {@link android.app.Fragment#onDestroy()}} or {@link
   * android.app.Activity#onDestroy()} is called.
   */
  void onDestroy();
}

load();

在调用Glide.with()的情况下获得一个requestManager ,会调用这个方法 , 

public RequestBuilder<Drawable> load(@Nullable Object model) {
  return asDrawable().load(model);
}

这里并没有直接加载我们获得的Url资源,而是调用asDrawable来配置资源,其实就是说加载的图片是通过drawable,bitmap或者是Gif进行图片显示,图片默认的事drawable格式,你也可以使用asGif或者是asBitmap。

public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class).transition(new DrawableTransitionOptions());
}

使用load()方法主要是为了获得RequestBuilder对象

protected RequestBuilder(Glide glide, RequestManager requestManager,
    Class<TranscodeType> transcodeClass) {
  this.glide = glide;
  this.requestManager = requestManager;
  this.context = glide.getGlideContext();
  this.transcodeClass = transcodeClass;
  this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
  this.requestOptions = defaultRequestOptions;
}

完成asDrawable方法对RequestBuilder的创建后才调用load方法来传递我们的url地址,其实在load也没有做什么事,就是一个中转站,转给了loadGeneric方法

@SuppressWarnings("unchecked")
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
  return loadGeneric(model);
}

private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  this.model = model;
  isModelSet = true;
  return this;
}

into();

Into()加载图片资源,可以说这个方法是这两个方法里面最难的一个,不管多难我们都要学习。通过这个方法,可以看出来,把传进去的view转化为Target,至于什么样的Target,会根据图片的类型而定

public Target<TranscodeType> into(ImageView view) {
  Util.assertMainThread();
  Preconditions.checkNotNull(view);

  if (!requestOptions.isTransformationSet()
      && requestOptions.isTransformationAllowed()
      && view.getScaleType() != null) {
    if (requestOptions.isLocked()) {
      requestOptions = requestOptions.clone();
    }
    switch (view.getScaleType()) {
      case CENTER_CROP:
        requestOptions.optionalCenterCrop();
        break;
      case CENTER_INSIDE:
        requestOptions.optionalCenterInside();
        break;
      case FIT_CENTER:
      case FIT_START:
      case FIT_END:
        requestOptions.optionalFitCenter();
        break;
      case FIT_XY:
        requestOptions.optionalCenterInside();
        break;
      case CENTER:
      case MATRIX:
      default:
        // Do nothing.
    }
  }

会先判断当前的Target对象有没有Request,如果有的话,直接调用clear()方法清除掉,以节省资源,然后创建新的Request并添加到新的Target上面。

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
  Util.assertMainThread();
  Preconditions.checkNotNull(target);
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }

  Request previous = target.getRequest();

  if (previous != null) {
    requestManager.clear(target);
  }

  requestOptions.lock();
  Request request = buildRequest(target);
  target.setRequest(request);
  requestManager.track(target, request);

  return target;
}

关于这个into方法,要是细谈的话,可能看着看着就蒙了,为了让大家好好的理解,就取其中的精华来给大家讲吧,如果需要,可以去深入的了解。

在into();方法里面内存应用:

    1,从Lrucache中取,若有数据,那么就在engine类的map中存一份这个资源,key和Lrucache的key是类似的,但是值是弱引用的Lrucache资源,这样就减少了Lrucache可能要移除最近最少使用的图片,然后就减少了再去重新加载的问题,因为它可以去map中去一下图片
    2,若Lrucache里没有取到图片,就通过刚才的map中去取。
    3,创建一个engineJob去硬盘中读取资源,去判断硬盘缓存策略是什么类型的,然后取出指定类型的图片。

接下来介绍一下Glide的常用方法,以及用Glide对内存的优化:

先介绍一下可以导入的几种图片格式:

// 加载本地图片
File file = new File(getExternalCacheDir() + "/demo.jpg");
Glide.with(this).load(file).into(imageView);

// 加载应用资源
Glide.with(this).load(R.drawable.image).into(imageView);

// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);

// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);

占位图

  Glide.with(MainActivity.this)
     .load(url)
     .placeholder(R.mipmap.placeholder)
     .into(mImage);

异常占位图

//错误的图片地址
String url = "http://sc.net/uploads/allimg/150709/14-150FZ94211O4.jpg";
Glide .with(context)
     .load(url)
     .placeholder(R.mipmap.defalut) //设置占位图 
     .error(R.mipmap.error) //设置错误图片
     .crossFade() //设置淡入淡出效果,默认300ms,可以传参 //.dontAnimate() //不显示动画效果
     .into(imageView);

设置下载优先级

• Priority.LOW
• Priority.NORMAL
• Priority.HIGH
• Priority.IMMEDIATE

Glide
    .with(context)
    .load(url)
    .priority(Priority.NORMAL)
    .into(imageView);

设置图片大小和缩放形式

Glide 会根据ImageView的大小,自动限制图片缓存和内存中的大小,当然也可以通过调用override(horizontalSize, verticalSize)限制图片的大小:

Glide.with(context)
       .load(url)
       .override(200, 200) 
       .into(imageView);

Glide支持两种图片缩放形式,CenterCrop 和 FitCenter
CenterCrop:等比例缩放图片,直到图片的宽高都大于等于ImageView的宽度,然后截取中间的显示。

Glide
    .with(context)
    .load(url)
    .override(200, 200) 
    .centerCrop() 
    .into(imageView);

FitCenter:等比例缩放图片,宽或者是高等于ImageView的宽或者是高

Glide
    .with(context)
    .load(url)
    .override(200, 200)
    .fitCenter() 
    .into(imageView);

设置监听请求接口

Glide.with(this).load(imageUrl).listener(new RequestListener<String, GlideDrawable>() { 设置监听的用处 可以用于监控请求发生错误来源,以及图片来源 是内存还是磁盘

    @Override

public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {

        return false;
    }

    @Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        return false;
    }
}).into(imageView);

Glide优化方法

清除缓存的方法

Glide.get(this).clearDiskCache();//清理磁盘缓存 需要在子线程中执行
Glide.get(this).clearMemory();//清理内存缓存  可以在UI主线程中进行

清除掉所有的图片加载请求

Glide.clear()

滚动加载,不滚动时不加载,提高listview效率,类似于滑动锁,节省流量,提高效率

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState){
        case SCROLL_STATE_FLING:
            Log.i("ListView","用户在手指离开屏幕之前,由于滑了一下,视图仍然依靠惯性继续滑动");
            Glide.with(getApplicationContext()).pauseRequests();
            //刷新
            break;
        case SCROLL_STATE_IDLE:
            Log.i("ListView", "视图已经停止滑动");
            Glide.with(getApplicationContext()).resumeRequests();
            break;
        case SCROLL_STATE_TOUCH_SCROLL:
            Log.i("ListView","手指没有离开屏幕,视图正在滑动");
            Glide.with(getApplicationContext()).resumeRequests();
            break;
    }
}

内存缓存

设置跳过内存缓存

Glide
    .with(context)
    .load(url)
    .skipMemoryCache(true)
    .into(imageView);

设置缓存策略(磁盘缓存)

策略解说:
        all:缓存源资源和转换后的资源
        none:不作任何磁盘缓存
        source:缓存源资源
        result:缓存转换后的资源

Glide
    .with(context)
    .load(url)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(imageView);

在后台线程当中进行加载和缓存

为了保证Glide在后台线程当中加载资源文件更加容易,Glide除了Glide.with(fragment).load(url).into(view)之外还提供了

downloadOnly(int width, int height)
downloadOnly(Y target)// Y extends Target<File>
into(int width, int height)
1.downloadOnly

Glide的downloadOnly()允许开发者将Image的二进制文件下载到硬盘缓存当中,以便在后续使用,
在UI线程当中异步下载,在异步线程当中则是使用width和height
在异步线程当中同步调用下载,在同步线程当中,
downloadOnly使用一个target作为参数

1、 在后台线程当中下载图片,可以通过如下的方式:
FutureTarget<File> future = Glide.with(applicationContext)
    .load(yourUrl)
    .downloadOnly(500, 500);
    File cacheFile = future.get();
当future返回的时候,image的二进制文件信息就存入了disk缓存了,值得注意的是downloadOnly API只是保证图片个bytes数据在disk当中是有效的。

2、下载完毕之后如果想要进行显示,可以通过如下方式进行调用:

Glide.with(yourFragment)
    .load(yourUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(yourView);

Glide.with(yourFragment)
    .load(yourUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(yourView);
通过DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保证程序会去读取缓存文件

如果想要在后台线程当中获取某个URL对应的Bitmap
不通过downloadOnly,可以使用into(),会返回一个FutureTarget对象,比如,想要得到一个URL对应的500*500的centerCrop裁剪图片,可以通过如下方式实现:
Bitmap myBitmap = Glide.with(applicationContext)
    .load(yourUrl)
    .asBitmap()
    .centerCrop()
    .into(500, 500)
    .get()

大家们需要注意:上面的调用只能在异步线程当中,如果在main Thread当中调用.get(),会阻塞主线程

讲到这里有点收不住了,再给大家介绍一下,图片加载框架,picasso,Glide,Fresco(FB)的区别:

     Picasso :和Square的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。

    Glide   :模仿了PicassoAPI,而且在他的基础上加了很多的扩展(比如gif等支持)Glide默认的Bitmap格式是RGB_565,比    Picasso默认的ARGB_8888格式的内存开销要小一半;Picasso缓存的是全尺寸的(只缓存一种),而Glide缓存的是跟ImageView尺寸相同的(56*56128*128是两个缓存)

    FB的图片加载框架Fresco:最大的优势在于5.0以下(最低2.3)bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem)。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。为什么说是5.0以下,因为在5.0以后系统默认就是存储在Ashmem区了。

总结:

  Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多如果项目中网络请求本身用的就是okhttp或者retrofit(本质还是okhttp),那么建议用Picasso,体积会小很多(Square全家桶的干活)。Glide的好处是大型的图片流,比如gif、Video,如果你们是做美拍、爱拍这种视频类应用,建议使用。
Fresco在5.0以下的内存优化非常好,代价就是体积也非常的大,按体积算Fresco>Glide>Picasso
不过在使用起来也有些不便(小建议:他只能用内置的一个ImageView来实现这些功能,用起来比较麻烦,我们通常是根据Fresco自己改改,直接使用他的Bitmap层)

(有不足的希望大家给我评论)

猜你喜欢

转载自blog.csdn.net/nazicsdn/article/details/79776990