Android: Glide笔记

//该死的拖延症,总是要学习做笔记,纸上得来终觉浅。

一、简介、使用。

1.1简介

略,见 主页

1.2基本使用

Glide.with(imageView.getContext())
            .load(url)
            .placeholder(defaultImage)
            .error(failImage) .diskCacheStrategy(DiskCacheStrategy.ALL)
            .into(imageView);

其中 load()重载方法。load也支持多参数,这里我们可以传入的有Uri、File、byte[]、Object、Bitmap、String、Drawable、Integer、URl,

DiskCacheStrategy是缓存策略,有以下几个参数,并详细说明该策略的保存信息。

   1)  ALL 网络资源执行DATA、RESOURCE,本地资源执行RESOURCE
    2) NONE  不缓存任何内容
    3) DATA  缓存原始图片
    4) RESOURCE  缓存解码后的图片
    5) AUTOMATIC  根据图片资源智能地选择使用哪一种缓存策略(默认选项)

1.3 其他用法

1.3.1
listener 这里我们可以对加载图片进行处理,加载成功和加载失败两个方法
1.3.2
addListener 和listener不同的是,listener是清空了监听,把当前监听加入进去,addListener是不清空监听,既不影响其他地方调用的监听,仅仅是把当前的监听加入进去,建议使用addListener,其他和listener相同
1.3.3
.centerInside() .fitCenter() .centerCrop() 这三种图片处理方式,centerInside等比例缩小显示,在横向或竖向上相等,不会进行放大。fitCenter等比例放大或缩小。centerCrop等比例放大。
1.3.4
override(int w,int h)这个方法会将图片以制定的高宽显示在ImageView上
1.3.5
apply(RequestOptions.bitmapTransform(new RoundedCorners(dp2px(60)))) 这个方法可以设置为圆形图了。
.optionalFitCenter() 同样可以圆形

二、原理分析

2.1原理

缓存:
先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,如果LruCache中没有,则去WeakReference中寻找,如果WeakReference中有,则从WeakReference中取出图片使用,同时将图片重新放回到LruCache中,如果WeakReference中也没有图片,则去文件系统中寻找,如果有则取出来使用,同时将图片添加到LruCache中,如果没有,则连接网络从网上下载图片。图片下载完成后,将图片保存到文件系统中,然后放到LruCache中。
Glide的缓存只有两个模块,一个是内存缓存,一个是磁盘缓存。其中内存缓存又分为Lru算法的缓存和弱引用缓存。

LruCache算法,Least Recently Used,又称为近期最少使用算法。主要算法原理就是把最近所使用的对象的强引用存储在LinkedHashMap上,并且,把最近最少使用的对象在缓存池达到预设值之前从内存中移除。
读取的顺序是:Lru算法缓存、弱引用缓存、磁盘缓存

写入的顺序是:弱引用缓存、Lru算法缓存、磁盘缓存(不准确)

2.2 代码顺序

2.2.1 with()

with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现。然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。

RequestManagerRetriever类中看似有很多个get()方法的重载,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。
a.传入Application参数
如果在Glide.with()方法中传入的是一个Application对象,那么这里就会调用带有Context参数的get()方法重载,调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。

b.非Application参数的情况。
最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要知道加载的生命周期。Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。

2.2.2 load()方法。

根据传入的参数类型作为泛型,创建一个图片请求对象DrawableTypeRequest.
DrawableTypeRequest的父类是DrawableRequestBuilder,DrawableRequestBuilder中有很多个方法,这些方法其实就是Glide绝大多数的API比如说placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等。

      RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    this.context = context.getApplicationContext();
    this.lifecycle = lifecycle;//与页面生命周期绑定的lifecycle对象
    /*treeNode:
     *提供了可以访问当前上下文中所有的RequestManager,也就是说基于Context层次结构建立了RequestManager的层次结构,
     *而上下文的层次结构是在Activity / Fragment中嵌套的。
     *不过,注意,如果当前上下文是Application Context ,只能访问当前上下文的RequestManager. 总而言之就是更方便的管理所有的RequestManager
     */
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;//请求管理控制类,start、pause、resume、clear、restart、addRequest...
    this.glide = Glide.get(context);//保存有一个单例的glide对象
    this.optionsApplier = new OptionsApplier();//optionsApplier的apply方法返回一个图片请求对象DrawableTypeRequest 
    /*网络状态的监听接口,实现了LifecycleListener ,与Activity/Fragment 生命周期绑定在一起。
     *具体实现DefaultConnectivityMonitor 以及NullConnectivityMonitor ,其中前者是一个网络监听的具体实现,
     *后者是一个空实现,类似于Null Object Pattern ,可以避免空指针异常
     */
    ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker));
  
    if (Util.isOnBackgroundThread()) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                lifecycle.addListener(RequestManager.this);
            }
        });
    } else {
        lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);
}

/**
 * 根据传入的参数类型作为泛型,创建一个图片请求对象DrawableTypeRequest
 * DrawableTypeRequest中存有 glide、requestTracker、lifecycle等的引用
 * 还有ModelLoader,load方法传入的数据不一定是url,传入的数据通过ModelLoader处理后转为真正的图片数据源
 */
public DrawableTypeRequest load(String string) {
    return (DrawableTypeRequest) fromString().load(string);
}
public DrawableTypeRequest fromString() {
    return loadGeneric(String.class);
}
private  DrawableTypeRequest loadGeneric(Class modelClass) {
    ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
    ModelLoader fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context);
    if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
        throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                + " Glide#register with a ModelLoaderFactory for your custom model class");
    }
    return optionsApplier.apply(new DrawableTypeRequest(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier));
}
 //DrawableTypeRequest的load方法只是把传入的model持有,并未真正加载
public GenericRequestBuilder load(ModelType model) {
    this.model = model;
    isModelSet = true;
    return this;
 }
2.2.3 into()

into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。

 public Target into(ImageView view) {
   Util.assertMainThread();
   if (view == null) {
       throw new IllegalArgumentException("You must pass in a non null View");
   }
  if (!isTransformationSet && view.getScaleType() != null) {
       switch (view.getScaleType()) {
           case CENTER_CROP:
            applyCenterCrop();
               break;
           case FIT_CENTER:
           case FIT_START:
           case FIT_END:
               applyFitCenter();
               break;
        //$CASES-OMITTED$
        default:
            // Do nothing.
       }
   }
   return into(glide.buildImageViewTarget(view, transcodeClass));

}

DrawableRequestBuilder的父类是GenericRequestBuilder,在这里根据显示拉伸类型,调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的。其实是调用的为ImageViewTargetFactory的buildTarget()方法。然后传递给GenericRequestBuilder另一个接收Target对象的into()方法当中了。

   public class ImageViewTargetFactory {

       @SuppressWarnings("unchecked")
       public  Target buildTarget(ImageView view, Class clazz) {
           if (GlideDrawable.class.isAssignableFrom(clazz)) {
               return (Target) new GlideDrawableImageViewTarget(view);
           } else if (Bitmap.class.equals(clazz)) {
        return (Target) new BitmapImageViewTarget(view);
           } else if (Drawable.class.isAssignableFrom(clazz)) {
        return (Target) new DrawableImageViewTarget(view);
           } else {
               throw new IllegalArgumentException("Unhandled class: " + clazz
                + ", try .as*(Class).transcode(ResourceTranscoder)");
           }
       }
   }

在buildTarget()方法中会根据传入的class参数来构建不同的Target对象,这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。
接上文第一次into(imageview)通过imageViewTargetFactory.buildTarget(imageView, transcodedClass)生成GlideDrawableImageViewTarget对象有传递给第二个into(),第二个into对象

 public > Y into(Y target) {
    Util.assertMainThread();
    if (target == null) {
        throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
        throw new IllegalArgumentException("You must first set a model (try #load())");
    }
    Request previous = target.getRequest();
    if (previous != null) {
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
    Request request = buildRequest(target);
    target.setRequest(request);
    lifecycle.addListener(target);
     requestTracker.runRequest(request);
    return target;
}
2.2.4 request

可以看出来调用buildRequest()方法构建出了一个Request对象,然后执行这个Request。Request是用来发出加载图片请求的,buildRequestRecursive实则调用了buildRequestRecursive函数。
顺序为如果配置了thumbnail(缩略图)请求,则构建一个ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)请求,否则简单的构建一个Request,调用了obtainRequest函数。至此请求对象创建成功。
然后执行runrequest

 public void runRequest(Request request) {
      this.requests.add(request);
//添加request对象到集合中,requests是一个set,Set是一个不包含重复元素的 collection,在这里是无序的。
    if(!this.isPaused) {
        request.begin();//如果当前状态是非暂停的,调用begin方法发送请求
    } else {
        this.pendingRequests.add(request);
  //将请求加入到挂起的请求集合,RequestTracker在RequestManager类中初始化,并且跟踪着生命周期
    }

}

begin函数,首先做了几个判断,验证宽高是否合法,加载占位图等,然后执行onsizeReady()方法中load() ,进入Engine逻辑

 this.engine.load(this.signature, width, height, dataFetcher, this.loadProvider, this.transformation, transcoder, this.priority, this.isMemoryCacheable, this.diskCacheStrategy, this);

load的策略,据调用loadFromCache从内存加载,若返回值为空再次从活动的资源中加载,若再次为空查看jobs是否提交过任务,若没有提交则创建EngineRunnable,并将任务提交到engineJob中。
Transformation类负责处理资源,这里面出现BitmapPool类,达到Bitmap复用。
先从cache中寻找资源,如果找到则将其从cache中移除并放入activeResources中,否则从activeResources中寻找。cache是LruResourceCache对象,作为资源的LRU缓存;activeResources是以弱引用为值的Map,用于缓存使用中的资源。比一般内存缓存额外多一级缓存的意义在于,当内存不足时清理cache中的资源时,不会对使用中的Bitmap造成影响。

3.1

with(Context context) - 需要上下文,这里还可以使用 Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 的对象。将 Activity/Fragment 对象作为参数的好处是,图片的加载会和 Activity/Fragment 的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是 Context。

3.2Module

Glide 的 Module 是一个可以全局改变 Glide 的行为的东西,为了定制 Glide 的行为我们要去实现 interface GlideModule 来写我们自己的代码。

public class ExampleModule implements GlideModule{
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // todo
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        // todo
    }
}

可以看到 GlideModule 为我们提供了两个方法,这里我们主要使用的是 applyOptions(Context context, GlideBuilder builder) , 我们自己的需要重新定义的代码写在该方法里就可以了。然后我们还需要去 AndroidManifest.xml 中使用 meta 声明我们上面实现的 Module

   

    

    ...


applyOptions(Context context, GlideBuilder builder) 中有两个参数, 我们通过使用 GlideBuilder 来实现我们的需求。先看看 GlideBuilder 中可用的方法

.setMemoryCache(MemoryCache memoryCache)
.setBitmapPool(BitmapPool bitmapPool)
.setDiskCache(DiskCache.Factory diskCacheFactory)
.setDiskCacheService(ExecutorService service)
.setResizeService(ExecutorService service)
.setDecodeFormat(DecodeFormat decodeFormat)

3.3Transformations篇

图片进行处理操作,比如:图片切圆角,灰阶处理等等;这些需求我们通过 Transformations 操作 bitmap 来实现,我们可以修改图片的任意属性:尺寸,范围,颜色,像素位置等等。其实我们之前已经提到过两个 Transformation 了,即 fitCenter 和 centerCrop ,这两个是 Glide 已经实现的。

怎么样来实现自己的 Transformation ,我们需要创建一个类去实现 Transformation 接口,但是要实现这个方法还是比较复杂的,接口中 transform 方法提供的参数 Resource resource 不是那么好处理的。如果你只是想要对图片(不是 Gif 和 video)做常规的 bitmap 转换,我们推荐你使用抽象类 BitmapTransformation。它简化了很多的实现,这应该能覆盖 95% 的应用场景啦。

下面的代码实现了对图片切圆角的操作,其中 getId() 方法描述了这个 Transformation 的唯一标识,为避免意外我们需要确保它是唯一的。

public class RoundTransformation extends BitmapTransformation     {
    private float radius = 0f;

    public RoundTransformation(Context context) {
        this(context, 4);
    }

    public RoundTransformation(Context context, int px) {
        super(context);
        this.radius = px;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }

    private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null)
            return null;

       Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
       paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
         RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }

}

使用
调用 .transform() 方法,将自定义的 Transformation 的对象作为参数传递进去就可以使用你的 Transformation 了,这里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的转换。

Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20))
//.bitmapTransform( new RoundTransformation(context , 20) )
.into(mImageView);

你可能感兴趣的:(Android: Glide笔记)