Android Glide

1.Glide

Glide是Google主导的图片加载开源库。它稳定、速度快、可自适应图片尺寸、支持众多格式、支持加载不同来源的图片、内存和磁盘缓存的优化等。

Glide优势:

①多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)

②生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)。Glide可以感知调用页面的生命周期

③高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)

④高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)

2.Glide的使用

①Glide的一个完整的请求至少需要三个参数:

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

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

load(String url) - 这里使用的是一个图片的URL

into(ImageView imageView) - 需要显示图片的目标 ImageView

②占位图设置

Glide.with(context).load(url)

    .placeholder(R.drawable.place_image)

    .error(R.drawable.error_image)

    .fallback(R.drawable.fallback_image)

    .into(imageView);

placeholder :指定加载前显示的图片资源。

error:指定加载失败显示的图片资源。

fallback:指定传递加载资源为null的时候,显示的图片资源。

注:这里需要注意一点,placeholder() 和 error() 的参数都是只支持 int 和 Drawable 类型的参数,这种设计应该是考虑到使用本地图片比网络图片更加合适做占位图。

③裁剪缩放

在项目开发过程中,指定图片显示大小可能用到,毕竟从服务器获取的图片不一定都是符合设计图的标准的。在这里就可以使用 override(width,height)方法,在图片显示到ImageView之前,重新改变图片大小。

Glide.with(context).load(url)

    .override(width,height)//这里的单位是px

    .into(imageView);

ImageView本身提供了ScaleType属性,这个属性可以控制图片显示时的方式。

Glide也提供了两个类似的方法CenterCrop()和 FitCenter()。

CenterCrop()方法是将图片按比例缩放到足以填充 ImageView的尺寸,但是图片可能会显示不完整;

FitCenter()则是图片缩放到小于等于ImageView的尺寸,这样图片是显示完整了,但是ImageView就可能不会填满了。

注:其实Glide的CenterCrop()和FitCenter()这两个方法分别对应ImageView的ScaleType属性中的CENTER_CROP和FIT_CENTER命名基本一致。

④缩略图

Glide的缩略图功能和占位图略有不同,占位图必须使用资源文件才行,而缩略图是动态的占位图可以从网络中加载。缩略图会在事件请求加载完成或者处理完之后才显示。在原始图片到达之后,缩略图不会取代原始图片,只会被抹除。

Glide 为缩略图提供了2种不同的加载方式,比较简单的方式是调用thumbnail()方法,参数是 float 类型,作为其倍数大小。例如,传入0.2f作为参数,Glide将会显示原始图片的20%的大小,如果原图是1000x1000的尺寸,那么缩略图将会是 200x200的尺寸。为缩略图明显比原图小得多,所以我们需要确保ImageView的ScaleType设置的正确。

Glide.with(context).load(url).thumbnail(0.2f ).into(imageView);

注:应用于请求的设置也将应用于缩略图。

使用thumbnail()方法来设置是简单粗暴的,但是如果缩略图需要通过网络加载相同的全尺寸图片,就不会很快的显示了。所以Glide提供了另一种防止去加载缩略图。

private void loadImageThumbnailRequest(){

    DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );

    Glide.with( context ).load( url )

        .thumbnail( thumbnailRequest )

        .into( imageView );

}

与第一种方式不同的是,这里的第一个缩略图请求是完全独立于第二个原始请求的。该缩略图可以是不同的资源图片,同时也可以对缩略图做不同的转换,等等…

⑤载入动画

Glide在显示图片的时候,为了让显示效果不那么突兀,会以一种更柔和的方式去显示,就会在加载的时候给一个动画效果,它可以使用 crossFade()方法进行配置,如果不特殊处理,默认它是开启的,并且本身默认动画的时长是 300ms。

crossFade()的效果是默认开启的,所以如果不需要这样的一个动画效果,可以使用dontAnimate() 来禁用动画效果。

Glide.with(context).load(url)

    .dontAnimate()

    .placeholder(R.drawable.place_image)

    .error(R.drawable.error_image)

    .into(imageView);

⑥缓存处理

为了更快的加载图片,肯定希望可以直接拿到图片,而不是进行网络请求,所以需要缓存。Glide 通过使用默认的内存和磁盘缓存来避免不必要的网络请求。

Glide.with(context).load(url)

    .skipMemoryCache(false)

    .diskCacheStrategy(DiskCacheStrategy.NONE)

    .into(imageView);

内存缓存是默认开启的,.skipMemoryCache(false)可以关闭。

磁盘缓存是默认开启,.diskCacheStrategy( DiskCacheStrategy strategy )可以关闭。

DiskCacheStrategy 的枚举意义:

DiskCacheStrategy.NONE 什么都不缓存

DiskCacheStrategy.SOURCE 只缓存全尺寸图

DiskCacheStrategy.RESULT 只缓存最终的加载图

DiskCacheStrategy.ALL 缓存所有版本图(默认)

⑦优先级

对于同一个页面,如果需要在多个地方都加载线上图片,必然会存在一个优先级的问题。例如:正常来说,背景图是比其他图片优先级更高的图片。

Glide 是可以在加载中,对当前加载的图片,调整加载的优先级的。需要使用.priority() 方法,它可以接受一个 Priority 的枚举类型,包含四种值:LOW(低)、HIGH(高)、NORMAL(普通)、IMMEDIATE(立即)。

⑧支持 Gif & 视频

Glide 支持 Gif

Glide.with(context).load(gifUrl)

    .asGif()

    .error( R.drawable.error )

    .into( imageView );

.asGif(): 校验它是否是一个 Gif ,如果不是,则认为是一次错误的加载。这个时候就可以使用 asGif() 来进行校验,如果当前加载的图片不是一个正确的 Gif 格式,则会去显示 error() 配置的图片。

有时候可能只为显示一张图片,可以强制显示Gif 图片的第一帧,用asBitmap()方法替代asGif()即可。

Glide还能显示视频(只能显示手机本地的视频)。但它和Gif显示的效果不一样的一点在于,它不会去播放视频文件,而只是将视频文件的第一帧做为一个图片去显示出来。

String filePath = "/storrage/emulated/0/Pictures/video.mp4";

Glide.with( context )

    .load( Uri.fromFile( new File( filePath ) ) )

    .into( imageView );

⑨加载监听
Glide 加载的图片的结果可以进行监听,使用 listener() 方法设置一个监听器,它接收一个 RequestListener 的接口。

RequestListener requestListener = new RequestListener() {

    @Override

    public boolean onLoadFailed(GlideException e, Object model, Target target, boolean isFirstResource) {

        return false;

    }

    @Override

    public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {

        return false;

    }

};

Glide.with(context).load(url)

 .listener(requestListener)

 .into(imageView);

如果需要监听图片加载错误的原因,可以在 onLoadFailed() 中做处理。

需要注意的是,这两个方法的返回值,最好都是 false。因为如果返回 true ,将表示你已经处理了这次的事件,而 Glide 将不会再做额外的处理。例如,如果 onLoadFailed() 返回了 true 的话,在图片加载失败之后,error() 中设置的图片,并不会被显示,因为 Glide 认为开发者已经在外部对这个错误进行了处理。

⑩变换加载的图片
对于使用Glide加载的图片,如果想要在其显示之前,对其进行一些变换操作,例如,改变颜色、虚化、圆角子类的,都需要用到transfrom()方法,它主要用于支持在图片显示之前,自定义的变换效果。

变换有两个方法:

transfrom():它可以添加一个通用的变换效果。

bitmapTransfrom():限制了变换的类型,只能设置 Bitmap 的变换。

3.Glide源码解析

以Glide.with(activity)方法为切入点开始分析源码:

public static RequestManager with( FragmentActivity activity) {

    return getRetriever(activity).get(activity); 

}

with()方法返回的是RequestManager实例,获取RequestManager分两步:

①第一步getRetriever(activity):先获取到RequestManagerRetriever实例。

//1. 单例模式创建Glide实例

//2. 获取RequestManagerRetriever实例

private static RequestManagerRetriever getRetriever(Context context) {

    Preconditions.checkNotNull(context, "You cannot start a load on a not yet attached View or a Fragment where getActivity() returns null (which usually occurs when getActivity() is called before the Fragment is attached or after the Fragment is destroyed).");

    return Glide.get(context). getRequestManagerRetriever(); //分两步分析

}

1)使用单例模式获取Glide实例

public static Glide get(Context context) {

    if (glide == null) {

      GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules( context.getApplicationContext());

      synchronized (Glide.class) {

        if (glide == null) {

          checkAndInitializeGlide(context, annotationGeneratedModule);

        }

      }

    }

    return glide;

}

2)继续追踪调用链:checkAndInitializeGlide() -> initializeGlide()

private static void initializeGlide(Context context, GlideBuilder builder, GeneratedAppGlideModule annotationGeneratedModule) {

    ……

    Glide glide = builder.build(applicationContext); //建造者模式创建Glide实例

    ……

    Glide.glide = glide;

}

3)最终调用GlideBuilder.build()方法构建Glide实例

Glide build(Context context) {

   //此处省略初始化默认配置信息代码

    ……

    //创建实例并赋值给Glide成员变量requestManagerRetriever 。这里传入的requestManagerFactory用来创建RequestManager的工厂

    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory, experiments);

    return new Glide(context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel,  defaultRequestOptionsFactory, defaultTransitionOptions, defaultRequestListeners, experiments);

}

获取RequestManagerRetriever小结:

RequestManagerRetriever的创建是在Glide的初始化过程中完成的;

RequestManagerRetriever构造方法内传入requestManagerFactory,用来创建RequestManager。

②第二步 get(activity)创建并返回RequestManager实例

public RequestManager get(FragmentActivity activity) {

    if (Util.isOnBackgroundThread()) { 

      return get(activity.getApplicationContext());

    } else {

      assertNotDestroyed(activity);

      frameWaiter.registerSelf(activity);

      FragmentManager fm = activity.getSupportFragmentManager();

      return supportFragmentGet(activity, fm, null, isActivityVisible(activity));

    }

}

1)如果当前线程不是主线程,直接转到get(activity.getApplicationContext())中执行;否则继续调用supportFragmentGet()方法。

private RequestManager supportFragmentGet( Context context, FragmentManager fm, Fragment parentHint, boolean isParentVisible) {

    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);

    RequestManager requestManager = current.getRequestManager();

    if (requestManager == null) {

      Glide glide = Glide.get(context);

      //工厂模式创建requestManager

      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);

      if (isParentVisible) {

        requestManager.onStart();

      }

      current.setRequestManager( requestManager); //requestManager存储到隐藏的Fragment

    }

    return requestManager;

}

2)RequestManagerFactory初始化过程

RequestManagerRetriever.java

//默认RequestManagerFactory 

private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {

    @Override

    public RequestManager build(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode requestManagerTreeNode, Context context) {

       return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);

    }

};

public RequestManagerRetriever( RequestManagerFactory factory, GlideExperiments experiments) {

    this.factory = factory != null ? factory : DEFAULT_FACTORY; 

    handler = new Handler( Looper.getMainLooper(), this );

    frameWaiter = buildFrameWaiter( experiments);

}

默认使用DEFAULT_FACTORY来创建RequestManager。

3)RequestManager构造方法

RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) {

    ……

    if (Util.isOnBackgroundThread()) {

        Util.postOnUiThread(addSelfToLifecycle);

    } else {

        lifecycle.addListener(this);

    }

    lifecycle.addListener(connectivityMonitor);

}

private final Runnable addSelfToLifecycle = new Runnable() {

    @Override

    public void run() {

          lifecycle.addListener( RequestManager.this);

    }

};

把当前RequestManager添加到lifecycle,lifecycle是ActivityFragmentLifecycle类型,在SupportRequestManagerFragment构造方法中创建。

重点类总结:

①RequestManager implements LifecycleListener

操作和管理Glide加载图片的请求;

实现LifecycleListener接口,根据Activity、Fragment生命周期变化和网络状态来控制图片加载流程;

由RequestManagerRetriever统一创建和管理,创建时使用工厂模式;

②RequestManagerRetriever

作用是创建和复用RequestManager:使用RequestManagerRetriever.RequestManagerFactory工厂创建RequestManager,复用SupportRequestManagerFragment 中已有的RequestManager;

在Glide初始化时创建;

③SupportRequestManagerFragment 无视图的Fragment

安全存储RequestManager,由RequestManagerRetriever负责绑定;

添加到当前Activity,利用SupportRequestManagerFragment的生命周期变化来控制图片的加载流程;

4.RequestManager如何感知组件的生命周期

Glide感知生命周期的方法是添加隐藏的SupportRequestManagerFragment,继续查看getSupportRequestManagerFragment源码:

RequestManagerRetriever.java:

final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>(); //缓存已经添加的隐藏SupportRequestManagerFragment

private SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm, Fragment parentHint) {   

    SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);

    if (current == null) {

        current=(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

        if (current == null) {

            current = new SupportRequestManagerFragment();

           current.setParentFragmentHint( parentHint);

           pendingSupportRequestManagerFragmen ts.put(fm, current);

           fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();

           handler.obtainMessage(ID_REMOVE_SUPP ORT_FRAGMENT_MANAGER, fm).sendToTarget();

        }

    }

    return current;

}

首先检查是否已经添加了隐藏的SupportRequestManagerFragment,使用HashMap缓存SupportRequestManagerFragment 。然后查找FragmentManager中是否有可以复用的SupportRequestManagerFragment。如果都没有找到,创建一个新的SupportRequestManagerFragment,并且添加到pendings列表&&提交到FragmentManager。

现在隐藏的SupportRequestManagerFragment已经添加成功,继续分析SupportRequestManagerFragment.java源码:

private final ActivityFragmentLifecycle lifecycle;

//构造方法

public SupportRequestManagerFragment() {

    this(new ActivityFragmentLifecycle());

}

public SupportRequestManagerFragment( ActivityFragmentLifecycle lifecycle) {

    this.lifecycle = lifecycle;

}

……

@Override

public void onStart() {

    super.onStart();

    lifecycle.onStart();

}

@Override

public void onStop() {

    super.onStop();

    lifecycle.onStop();

}

@Override

public void onDestroy() {

    super.onDestroy();

    lifecycle.onDestroy();

    unregisterFragmentWithRoot();

}

新建一个ActivityFragmentLifecycle实例赋值给SupportRequestManagerFragment的lifecycle成员变量,查看ActivityFragmentLifecycle类:

class ActivityFragmentLifecycle implements Lifecycle {

    //储存LifecycleListener的列表

    private final Set<LifecycleListener> lifecycleListeners= Collections.newSetFromMap( new WeakHashMap<LifecycleListener, Boolean>());

    private boolean isStarted;

    private boolean isDestroyed;

    //向lifecycleListeners列表添加LifecycleListener

    @Override

    public void addListener(LifecycleListener listener) {

        lifecycleListeners.add(listener);

       //添加完成后,根据状态执行对应的回调方法

        if (isDestroyed) {

            listener.onDestroy();

        } else if (isStarted) {

            listener.onStart();

        } else {

            listener.onStop();

        }

    }

    @Override

    public void removeListener(LifecycleListener listener) {

        lifecycleListeners.remove(listener);

    }

    //遍历执行onStart

    void onStart() {

        isStarted = true;

        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {

            lifecycleListener.onStart();

        }

    }

    //与onStart类似

    void onStop() {

        ……

    }

    void onDestroy() {

        ……

    }

}

使用观察者模式,在组件生命周期发生变化的时候回调相应的操作方法。这一切都是框架完成的,在RequestManager构造函数中把自身加入到ActivityFragmentLifecycle的成员变量lifecycleListeners中。

RequestManager.java:

RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) {

    ……

    if (Util.isOnBackgroundThread()) {

        Util.postOnUiThread(addSelfToLifecycle); //切换到主线程执行

    } else {

        lifecycle.addListener(this); //添加到ActivityFragmentLifecycle的成员变量lifecycleListeners

    }

    lifecycle.addListener(connectivityMonitor);

}

private final Runnable addSelfToLifecycle = new Runnable() {

    @Override

    public void run() {

          lifecycle.addListener( RequestManager.this); //添加到ActivityFragmentLifecycle成员变量lifecycleListeners

    }

};

监听组件生命周期流程汇总:

①创建无视图SupportRequestManagerFragment 绑定到当前Activity;

SupportRequestManagerFragment持有RequestManager实例,RequestManager实例由RequestManagerRetriever创建并传递给SupportRequestManagerFragment ;

②RequestManager持有ActivityFragmentLifecycle引用,在SupportRequestManagerFragment 构造方法中产生并传递给RequestManager;

RequestManager在构造函数中把自己加入到ActivityFragmentLifecycle.lifecycleListeners列表

③生命周期同步过程,以Activity调用onStart()为例:

  1)Activity调用onStart()

  2)SupportRequestManagerFragment调用onStart()

@Override

public void onStart() {

    super.onStart();

    lifecycle.onStart(); //==ActivityFragmentLifecycle.onStart()

}

  3)ActivityFragmentLifecycle#onStart()

void onStart() {

    isStarted = true;

    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {

      lifecycleListener.onStart(); //==RequestManager.onStart()

    }

}

  4)RequestManager#onStart()

@Override

public synchronized void onStart() {

    resumeRequests(); //启动图片加载请求

    targetTracker.onStart(); //添加到TargetTracker,统一管理targets响应组件生命周期

}

图片加载时感知组件生命周期全过程:

1875d75052c14abdbc8be08ac16b4527.jpg

5.Glide如何管理图片加载请求

with()方法返回的是一个RequestManager对象,说明load()方法是在RequestManager类当中的,所以首先要看的就是RequestManager这个类。

cb60107feb314385a0eee7a5560e41ab.webp

 load()方法支持多种形式的图片来源,以RequestManager类中的 load(String string) 方法为例分析。

@Override

public RequestBuilder<Drawable> load(String string) {

    return asDrawable().load(string);

}

public RequestBuilder<Drawable> asDrawable() {

    return as(Drawable.class);

}

public <ResourceType> RequestBuilder<ResourceType> as( Class<ResourceType> resourceClass) {

    return new RequestBuilder<>(glide, this, resourceClass, context);

}

为了使逻辑更清晰,把上面三个方法合并成一行代码来分析:

RequestManager#load(String string)

//相当于

new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);

load(String string)最终被分为两步执行:

①以Drawable.class为参数创建RequestBuilder实例。

RequestBuilder构造函数:

protected RequestBuilder(Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) {

    this.glide = glide;

    this.requestManager = requestManager;

    this.transcodeClass = transcodeClass; //重要变量赋值为Drawable.class,构建ImageViewTarget的时候使用

    this.context = context;

    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);

    this.glideContext = glide.getGlideContext();

    initRequestListeners( requestManager.getDefaultRequestListeners()); //初始化默认请求监听

    apply(requestManager.getDefaultReque stOptions()); //应用默认配置信息

}

②调用RequestBuilder#load(String string)

public RequestBuilder<TranscodeType> load(String string) {

    return loadGeneric(string);

}

private RequestBuilder<TranscodeType> loadGeneric(Object model) {

    if (isAutoCloneEnabled()) {

        return clone().loadGeneric(model);

    }

    this.model = model; //String变量赋值给model

    isModelSet = true;

    return selfOrThrowIfLocked();

}

RequestBuilder重要成员变量:

transcodeClas = Drawable.class, 构建ImageViewTarget的时候使用;

model = 图片来源为String地址,构建Request的时候使用;

RequestManager#load(String string)分析到现在,似乎都是一些准备工作,真正的图片加载还没有出现。继续查看RequestBuilder#into(ImageView view)方法:

#RequestBuilder.java

public ViewTarget<ImageView, TranscodeType> into(ImageView view) {

    Util.assertMainThread(); //判断是否在主线程执行

    Preconditions.checkNotNull(view);

    //把ImageView的scaleType写进requestOptions 

    ……

    return into(

        //注释1:构建新的ImageViewTarget

        glideContext.buildImageViewTarget(view, transcodeClass),  null, requestOptions, Executors.mainThreadExecutor()); //主线程池,切回到主线程显示图片

  }

private <Y extends Target<TranscodeType>> Y into(Y target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {

    Preconditions.checkNotNull(target);

    if (!isModelSet) {

        throw new IllegalArgumentException("You must call #load() before calling #into()");

    }

    //注释2:构建新的加载请求Request 

    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest(); //获取目标View上已经存在的旧请求

    if (request.isEquivalentTo(previous) //两次的请求相同 && !(跳过内存缓存 && 旧请求已完成)

        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 

        if (!Preconditions.checkNotNull( previous).isRunning()) { //如果旧请求未开始

            previous.begin(); //启动旧请求

        }

        return target;

    }

    //取消Glide为target准备的所有加载请求,并释放已经加载的资源(例如Bitmap),以便它们可以被重用。

    requestManager.clear(target); 

    //利用View.setTag把request请求绑定到target指向的View

    target.setRequest(request); 

    //注释3

    requestManager.track(target, request);

    return target;

}

private boolean isSkipMemoryCacheWithCom pletePreviousRequest(

      BaseRequestOptions<?> options, Request previous) {

    return !options.isMemoryCacheable() && previous.isComplete();

}

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/125089885