Android Glide

article/2025/10/12 13:31:37

1.Glide

Glide是Google主导的图片加载开源库。它有很多优势:

①使用简单,链式调用。

②支持多种图片格式,如Gif、WebP、缩略图、Video等。

③支持生命周期集成。Glide可以感知调用页面的生命周期,根据Activity或Fragment的生命周期管理图片加载请求。

④支持内存缓存和磁盘缓存。Picasso只会缓存原始尺寸的图片,Glide可以缓存多种规格;Glide加载速度快且内存开销小(glide使用RGB565)。

⑤高效处理Bitmap。支持bitmap的复用和主动回收,减少系统回收压力。使用Glide加载图片时,Glide会默认根据View视图对图片进行压缩和转换,而不显示原始图。这也是Glide加载速度高于Picasso的原因。

Glide缺点:太大了(不是轻量级的)

 

2.Glide的使用

Glide图片加载流程:

4c88cdd8e3234091b7ee05f995741b7d.jpg

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

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

context上下文,可以使用Activity、Fragment、Context、ApplicationContext对象。推荐使用Activity或Fragment对象作为参数,这样图片的加载会和Activity/Fragment的生命周期保持一致,例如:onPaused时暂停加载,onResume时又会自动重新加载。而使用ApplicationContext时,图片加载会和application生命周期保持一致。

②占位图

    .placeholder(R.drawable.place_image) 加载前显示的图片资源。

    .error(R.drawable.error_image) 加载失败显示的图片资源。

    .fallback(R.drawable.fallback_image) 传递加载资源为null时显示的图片资源。

注意:placeholder()和error()的参数都只支持int和Drawable类型的参数,因为使用本地图片比网络图片更加合适做占位图。

③缩略图

缩略图会在事件请求加载完成或者处理完之后显示。在原始图片到达之后,缩略图不会取代原始图片,只会被抹除。

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

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

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

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

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

Glide.with(context).load(url)

    .thumbnail(thumbnailRequest)

    .into(imageView);

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

④载入动画

.crossFade()默认是开启的,并且本身默认动画的时长是300ms。

如果不需要默认动画效果,可使用.dontAnimate() 禁用动画效果。

⑤缓存处理

Glide的缓存分为内存缓存和硬盘缓存。缓存的读取顺序为:内存缓存 –> 磁盘缓存 –> 网络 ,内存缓存和磁盘缓存相互不影响,独立配置。

内存缓存:防止应用重复将图片读入到内存,造成内存资源浪费。内存缓存只缓存转换后的图片,而不是原始图片。内存缓存是默认开启的,可以通过skipMemoryCache(false)关闭。Glide的内存缓存使用的是LruCache算法,同时使用了弱引用机制。

磁盘缓存:防止应用重复从网络或其他地方下载和读取数据。磁盘缓存可根据用户设置来缓存原始图片或转换过后的图片。磁盘缓存默认是开启,可以通过设置diskCacheStrategy( DiskCacheStrategy.NONE)关闭

如果Glide不需要缓存,可以这样写:

Glide.with(context).load(url)

    .skipMemoryCache(false)

    .diskCacheStrategy(DiskCacheStrategy.NONE)

    .into(imageView);

DiskCacheStrategy枚举的意义:

DiskCacheStrategy.NONE 什么都不缓存

DiskCacheStrategy.DATA 只缓存原始图片

DiskCacheStrategy.RESOURCE 只缓存转换过后的图片

DiskCacheStrategy.ALL 既缓存原始图片,也缓存转换过后的图片

DiskCacheStrategy.AUTOMATIC 默认,Glide会根据图片资源智能地选择使用哪一种缓存策略

默认的存储路径是内置存储的沙盒环境cache/image_manager_disk_cache目录。

注:Glide加载一张图时,默认不会展示原图,而是会对图片进行压缩和转换,比如对图片进行尺寸压缩到目标View大小。

⑥支持Gif&视频

.asGif():校验是否是一个Gif,如果不是,则认为是一次错误的加载。使用asGif()进行校验,如果当前加载的图片不是一个正确的Gif格式,则会去显示error()配置的图片。有时候可能只为显示一张图片,可以强制显示Gif 图片的第一帧,用asBitmap()方法替代asGif()即可。

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

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

Glide.with(context )

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

    .into(imageView );

⑦加载监听

可以使用listener()方法对Glide加载图片的结果进行监听,它接收一个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认为开发者已经在外部对这个错误进行了处理。

⑧变换加载的图片

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

变换有两个方法:

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

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

 

3.源码解析Glide.with()

Glide.with()有多个重载方法,参数可以为Context、Activity、FragmentActivity、Fragment、View。

public static RequestManager with(Context context) { //与Application的生命周期相关联,将不受Activity/Fragment生命周期影响。该方法适用于在正常Activity/Fragment之外使用资源,例如在Service或notification中

    return getRetriever(context).get(context); 

}

public static RequestManager with(Activity activity) { //与activity生命周期相关联

    return getRetriever(activity).get(activity); 

}

public static RequestManager with( FragmentActivity activity) { //与FragmentActivity生命周期相关联

    return getRetriever(activity).get(activity); 

}

public static RequestManager with(Fragment fragment) { //与Fragment生命周期相关联

    return getRetriever(fragment).getContext().get( fragment); 

}

public static RequestManager with(View view) { //与包含该view的Fragment或Activity的生命周期相关联

    return getRetriever(view.getContext()).get( view); 

}

with()方法最终要返回RequestManager实例,获取RequestManager分为两步:第一步getRetriever()获取RequestManagerRetriever实例,第二步RequestManagerRetriever.get()获取RequestManager实例。

①第一步 getRetriever()获取RequestManagerRetriever实例。

private static RequestManagerRetriever getRetriever(Context context) {

    return Glide.get(context).getRequestManag erRetriever(); //创建Glide实例和RequestManagerRetriever实例,并返回RequestManagerRetriever实例

}

主要看使用单例模式获取Glide实例:

public static Glide get(Context context) {

    if (glide == null) {

      synchronized (Glide.class) {

        if (glide == null) {

          checkAndInitializeGlide(context, annotationGeneratedModule);

        }

      }

    }

    return glide;

}

private static void checkAndInitializeGlide(){

    initializeGlide(context, generatedAppGlideModule);

}

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

    initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);

}

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

    ……

    Glide glide = builder.build(applicationContext); //通过build创建Glide实例,在创建Glide实例过程中初始化了requestManagerRetriever实例

    Glide.glide = glide;

}

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

Glide build(Context context) {

   ……

    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory, experiments); //创建requestManagerRetriever对象,可见RequestManagerRetriever对象的创建是在Glide的初始化过程中完成的

    return new Glide(context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel,  defaultRequestOptionsFactory, defaultTransitionOptions, defaultRequestListeners, experiments);//创建Glide实例,并将新建的requestManagerRetriever实例赋值给Glide的requestManagerRetriever属性

}

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

②第二步 requestManagerRetriever.get()创建并返回RequestManager实例。

根据get()的参数不同,有不同的重载方法。

public RequestManager get(Context context) {

    if(Utils.isOnMainThread() && !(context instanceof Application)) {

        if(context instanceof FragmentActivity) {

            return get((FragmentActivity)context);

        } else if(context instanceof Activity) {

            return get((Activity)context);

        } else if(context instanceof ContextWrapper && ((ContextWrapper)context).getBaseContext( ).getApplicationContext() != null) {

            return get(((ContextWrapper)context).getB aseContext());

        }

    }

    return getApplicationManager(context);

}

public RequestManager get(FragmentActivity activity) {

    if (Util.isOnBackgroundThread()) { 

      return get(activity.getApplicationContext());

    } else { //添加一个空白Fragment,用于关联生命周期

      FragmentManager fm = activity.getSupportFragmentManager();

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

    }

}

public RequestManager get(Fragment fragment){

    if (Util.isOnBackgroundThread()) { 

      return get(fragment.getContext().getApplic ationContext());

    } else {

      FragmentManager fm = fragment.getChildFragmentManager();

      return supportFragmentGet( fragment.getContext(), fm, fragment, fragment.isVisible());

    }

}

public RequestManager get(Activity activity){

    if (Util.isOnBackgroundThread()) { 

      return get(activity.getApplicationContext());

    } else if(activity instanceof FragmentActivity) {

        return get((FragmentActivity)activity);

    } else {

      android.app.FragmentManager fm = activity.getFragmentManager();

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

    }

}

首先判断当前是否在主线程,如果不是主线程,直接转到get(activity.getApplicationContext())中执行;否则继续调用supportFragmentGet()方法添加空白Fragment。

这里有一个面试题:为什么不在子线程里调用Glide.with()?因为在子线程里不会去添加生命周期机制,只有在主线程中才会添加空白Fragment监听Activity/Fragment生命周期的变化。如果在子线程中调用with,那么图片加载的生命周期会与application一致,而不会随activity/fragment的生命周期变化而变化,也就是application销毁时图片才会停止加载。

(1)supportFragmentGet()方法

主要作用是创建空白Fragment,并返回它对应的RequestManager对象。

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

    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint); //创建空白Fragment

    RequestManager requestManager = current.getRequestManager();

    if (requestManager == null) {

      Glide glide = Glide.get(context);

      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); //工厂模式创建requestManager

      if (isParentVisible) {

           requestManager.onStart();

      }

      current.setRequestManager( requestManager); //requestManager存储到空白的Fragment中

    }

    return requestManager;

}

SupportRequestManagerFragment就是空白的Fragment,它用于安全存储RequestManager,空白Fragment会添加到当前Activity,利用空白Fragment的生命周期变化来控制图片的加载流程。

(2)getSupportRequestManagerFragment()方法

获取空白Fragment,Glide通过空白Fragment来感知生命周期。

RequestManagerRetriever.java:

final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>(); //缓存已添加的空白Fragment

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

    SupportRequestManagerFragment current =(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);//检查是否已经添加了空白Fragment

    if (current == null) {

        current=pendingSupportRequestManagerFra gments.get(fm);//查找FragmentManager中是否有可以复用的SupportRequestManagerFragment

        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(); //空白Fragment与Activity关联

        }

    }

    return current;

}

首先检查FragmentManager中是否有可以复用的空白Fragment,如果没有就查找pendings中是否已经添加了空白Fragment,如果还是没有找到就新建一个,并添加到pendings列表,同时提交到FragmentManager。

(3)SupportRequestManagerFragment类

现在空白的SupportRequestManagerFragment已经添加成功,看一下它的源码:

public class SupportRequestManagerFragment extends Fragment {

   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();

    }

}

在SupportRequestManagerFragment里新建了一个ActivityFragmentLifecycle实例赋值给空白Fragment的lifecycle成员变量。

(4)RequestManager类

RequestManager是由工厂模式创建的,上面代码中的factory是RequestManagerFactory类型。

默认使用DEFAULT_FACTORY来创建RequestManager。

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

    }

};

看一下RequestManager的构造方法:

public class RequestManager implements LifecycleListener {

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

        ……

        if (Util.isOnBackgroundThread()) {

            Util.postOnUiThread(addSelfToLifecycle);

        } else {

            lifecycle.addListener(this);

        }

        glide.registerRequestManager(this);

    }

    private final Runnable addSelfToLifecycle = new Runnable() {

        @Override

        public void run() {

              lifecycle.addListener( RequestManager.this);

        }

    };

 }

RequestManager实现了LifecycleListener接口,在RequestManager构造方法中,把当前空白Fragment对应的RequestManager添加到lifecycle。lifecycle是ActivityFragmentLifecycle类型,在SupportRequestManagerFragment构造方法中创建。

(5)ActivityFragmentLifecycle类

class ActivityFragmentLifecycle implements Lifecycle {

    private final Set<LifecycleListener> lifecycleListeners= Collections.newSetFromMap( new WeakHashMap<LifecycleListener, Boolean>()); //储存LifecycleListener的列表

    private boolean isStarted;

    private boolean isDestroyed;

    @Override

    public void addListener(LifecycleListener listener) {

        lifecycleListeners.add(listener); //向lifecycleListeners列表添加LifecycleListener

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

        if (isDestroyed) {

            listener.onDestroy();

        } else if (isStarted) {

            listener.onStart();

        } else {

            listener.onStop();

        }

    }

    @Override

    public void removeListener(LifecycleListener listener) {

        lifecycleListeners.remove(listener);

    }

    void onStart() {

        isStarted = true;

        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {//遍历执行onStart

            lifecycleListener.onStart();

        }

    }

    void onStop() {

        isStarted = false;

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

            lifecycleListener.onStop();

        }

    }

    void onDestroy() {

        isDestroyed = true;

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

            lifecycleListener.onDestroy();

        }

    }

}

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

 

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

①创建空白SupportRequestManagerFragment绑定到当前Activity。

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

RequestManager持有ActivityFragmentLifecycle引用。lifecycle在SupportRequestManagerFragment构造方法中产生并传递给RequestManager。

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

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

  1)Activity调用onStart()

  2)SupportRequestManagerFragment调用onStart()

@Override

public void onStart() {

    super.onStart();

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

}

  3)lifecycle调用onStart()

ActivityFragmentLifecycle#onStart()

void onStart() {

    isStarted = true;

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

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

    }

}

  4)RequestManager调用onStart()

RequestManager#onStart()

@Override

public synchronized void onStart() {

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

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

}

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

1875d75052c14abdbc8be08ac16b4527.jpg

 

5.Glide#load()

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

}

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

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

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

    this.glide = glide;

    this.requestManager = requestManager;

    this.transcodeClass = transcodeClass; //传进来的Drawable.class赋值给transcodeClass变量

   ……

}

②第二步 调用RequestBuilder#load(String string)

public RequestBuilder<TranscodeType> load(String string) {

    return loadGeneric(string);

}

private RequestBuilder<TranscodeType> loadGeneric(Object model) {

    this.model = model; //传进来的String赋值给model变量

    isModelSet = true;

    return selfOrThrowIfLocked();

}

RequestBuilder重要成员变量:

1)transcodeClass = Drawable.class, 构建ImageViewTarget的时候使用;

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

RequestManager#load(String string)只是新建了RequestBuilder,并给它的两个属性分别赋值。

 

6.Glide#into()

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

    ……

    return into(

        glideContext.buildImageViewTarget(view, transcodeClass),  //构建新的ImageViewTarget

null, requestOptions, 

Executors.mainThreadExecutor()); //主线程池,切回到主线程显示图片

  }

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

    Request request = buildRequest(target, targetListener, options, callbackExecutor); //构建新的加载请求Request 

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

    if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { //如果两次的请求相同 && !(跳过内存缓存 && 旧请求已完成),into()方法直接返回target

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

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

        }

        return target;

    }

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

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

    requestManager.track(target, request); //调用track方法

    return target;

}

private boolean isSkipMemoryCacheWithCom pletePreviousRequest(

      BaseRequestOptions<?> options, Request previous) {

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

}

track方法分析:

RequestManager.java:

synchronized void track(Target<?> target, Request request) {

    targetTracker.track(target); //target添加到targetTracker,管理target指向的View生命周期回调事件

    requestTracker.runRequest(request); //request添加到requestTracker,管理图片加载请求

}

targetTracker和requestTracker都是RequestManager的成员变量,分别负责管理target和request。

TargetTracker的职责是维护一个Target列表,统一管理所有Target的onStart()、onStop()和onDestroy()方法回调事件。

public final class TargetTracker implements LifecycleListener {

    private final Set<Target<?>> targets = Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>()); //维护target列表

    public void track(Target<?> target) {

        targets.add(target);

   }

   public void untrack(Target<?> target) {

       targets.remove(target);

   }

   @Override

   public void onStart() {

       for (Target<?> target : Util.getSnapshot(targets)) {

          target.onStart();

       }

   }

   @Override

   public void onStop() {

       for (Target<?> target : Util.getSnapshot(targets)) {

          target.onStop();

       }

   }

  @Override

   public void onDestroy() {

       for (Target<?> target : Util.getSnapshot(targets)) {

          target.onDestroy();

       }

   }

   public List<Target<?>> getAll() {

       return Util.getSnapshot(targets);

   }

   public void clear() {

       targets.clear();

   }

}

RequestTracker部分源码:

public class RequestTracker {

    private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>()); //如果Set直接持有request强引用,可能会发生内存泄漏,因此将request作为key存储在WeakHashMap里面,如果这些key不再使用,WeakHashMap会自动移除这些key,从而避免内存泄漏

    private final Set<Request> pendingRequests = new HashSet<>();  //直接持有未完成和等待开始的request强引用对象,防止在运行过程中被GC

    private boolean isPaused; //所有请求是否已经被暂停

  //把请求添加到requests和pendingRequests(Glide维护了两个队列,一个正在运行队列,一个等待执行队列)

    public void runRequest(Request request) {

        requests.add(request);

        if (!isPaused) {

            request.begin(); 

        } else {

            request.clear();

            pendingRequests.add(request);

        }

    }

   //停止正在运行的请求

    public void pauseRequests() {

        isPaused = true;

        for (Request request : Util.getSnapshot(requests)) {

            if (request.isRunning()) {

                request.pause();

                pendingRequests.add(request);

            }

        }

    }

   //开启未完成或失败的请求

    public void resumeRequests() {

        isPaused = false;

        for (Request request : Util.getSnapshot(requests)) {

            if (!request.isComplete() && !request.isRunning()) {

                request.begin();

            }

        }

        pendingRequests.clear();

    } 

}

综上,Glide管理图片加载请求的方式并不复杂:使用一个请求管理类RequestTracker维护所有请求列表,并且提供统一操作所有请求的方法(即所有请求的开启、暂停和重启等等方法),RequestManager只要通过一个RequestTracker对象就能轻轻松松控制图片加载请求。

 

6.图片加载请求执行流程

RequestTracker用来管理图片加载请求,来分析一下图片加载请求执行流程。

RequestTracker.java:

//把请求添加到requests和pendingRequests

public void runRequest(Request request) {

    requests.add(request);

    if (!isPaused) {

        request.begin(); //图片加载请求执行入口

    } else {

        request.clear();

        pendingRequests.add(request);

    }

}

request.begin()方法是请求开始执行的入口,这里的request是SingleRequest类型。

看一下SingleRequest类:

public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {

    private enum Status { //请求的状态

        PENDING, //已创建,但尚未执行

        RUNNING, 

        WAITING_FOR_SIZE, //Waiting for a callback given to the Target to be called to determine target dimensions

        COMPLETE, 

        FAILED,

        CLEARED, //Cleared by the user with a placeholder set, may be restarted

    }

    private final Object requestLock; //同步锁对象

    //监听请求的状态变化

   private final RequestListener<R> targetListener;

    private final RequestCoordinator requestCoordinator;//协调一个target上的多个请求

    private final Context context;

    private final Target<R> target;

    private final List<RequestListener<R>> requestListeners;

    private volatile Engine engine; //图片处理引擎

    @Override

    public void begin() {

        synchronized (requestLock) { 

            if (model == null) { //如果资源数据为null,执行onLoadFailed()方法

            onLoadFailed(new GlideException( "Received null model"), logLevel); //加载失败

            return;

        }

        if (status == Status.RUNNING) { //请求状态为RUNNING,不允许执行begin()方法

           throw new IllegalArgumentException( "Cannot restart a running request"); 

        }

        if (status == Status.COMPLETE) { //请求状态为COMPLETE时会执行onResourceReady方法

            onResourceReady(resource, DataSource.MEMORY_CACHE,  false);

            return;

        }

        experimentalNotifyRequestStarted(model);

        status = Status.WAITING_FOR_SIZE; //状态设为:等待target确定尺寸

        if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //如果通过RequestOption设置的宽高都大于0或者与Target原始宽高相等,则调用onSizeReady(overrideWidth, overrideHeight);;否则重新计算target尺寸,计算完成后依然会调用onSizeReady()方法

            onSizeReady(overrideWidth, overrideHeight);

        } else {

            target.getSize(this);

        }

        if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { //target设置占位图

            target.onLoadStarted( getPlaceholderDrawable());

        }

    }

  }

}

1)资源数据为null时会执行onLoadFailed(new GlideException("Received null model"), logLevel)方法:

private void onLoadFailed(GlideException e, int maxLogLevel) {

    synchronized (requestLock) { 

        loadStatus = null;

        status = Status.FAILED; //1.设置请求的状态为Status.FAILED

        setErrorPlaceholder(); //2.如果不是缩略图请求,setErrorPlaceholder()设置显示失败占位图

}

@GuardedBy("requestLock")

private void setErrorPlaceholder() {

    target.onLoadFailed(error); //3.告诉target资源加载失败,并把错误占位图资源回传给target

}

2)请求状态为COMPLETE时会执行onResourceReady(resource, DataSource.MEMORY_CACHE, false);方法:

public void onResourceReady(Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {

    Resource<?> toRelease = null;

    try {

        synchronized (requestLock) {

            loadStatus = null;

           onResourceReady((Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);

            }

        } finally {

           if (toRelease != null) {

               engine.release(toRelease);

          }

     }

}

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {

    status = Status.COMPLETE;

    target.onResourceReady(result, animation);

    notifyLoadSuccess();

}

3)如果通过RequestOption设置的宽高都大于0或者与Target原始宽高相等,则调用onSizeReady(overrideWidth, overrideHeight);否则重新计算target尺寸,计算完成后依然会调用onSizeReady()方法。

@Override

public void onSizeReady(int width, int height) {

    synchronized (requestLock) {

        if (status != Status.WAITING_FOR_SIZE) {

            return;

        }

        status = Status.RUNNING; //请求的状态设为RUNNING

        loadStatus = engine.load(glideContext, model, requestOptions.getSignature(),this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this, callbackExecutor); //图片加载引擎Engine开始加载图片

    }

}

到此为止,Request的begin()方法分析完毕,图片加载流程最终交给Engine执行。Engine是整个图片加载流程中一个非常重要的角色。

Engine.java:

public <R> LoadStatus load(GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) {

    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); //使用request配置信息构建EngineKey,用作资源缓存时的key

    EngineResource<?> memoryResource;

    synchronized (this) {

        memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //从正在使用的资源列表和内存缓存中查找资源

        if (memoryResource == null) { //缓存中查找不到资源,重用或新建一个新EngineJob

            return waitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime);

        }

    }

    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE, false);

     return null;

}

private EngineResource<?> loadFromMemory( EngineKey key, boolean isMemoryCacheable, long startTime) {

    if (!isMemoryCacheable) {

        return null;

    }

    EngineResource<?> active = loadFromActiveResources(key); //正在使用的资源列表中查找

    if (active != null) {

        return active;

    }

    EngineResource<?> cached = loadFromCache(key); //内存缓存中查找资源

    if (cached != null) {

        return cached;

    }

    return null;

}

内存缓存的流程图:

12e48d99ce51433d9eb71a5bd0709f8a.png

这里有个问题:LruCache只看到了取数据的过程,那什么时候存数据呢?其实存数据是在Engine.onResourceReleased()释放资源时,具体在于acquire引用计数器的作用:计数器>0说明图片正在使用,存在于activeResources弱引用map中;经过release()后若计数器=0说明图片不再被使用,就会调用上述方法释放资源,此时在该方法中会将当前没有使用的资源缓存到LruCache中。

注:Glide的图片缓存分为内存缓存和磁盘缓存,这里已经出现了内存缓存,磁盘缓存会在接下来的DecodeJob中处理。

private <R> LoadStatus waitForExistingOrStartNewJob(GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor, EngineKey key, long startTime) {

    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);

    if (current != null) {

        current.addCallback(cb, callbackExecutor);

        return new LoadStatus(cb, current);

    }

    EngineJob<R> engineJob = engineJobFactory.build(key,isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);

    DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext, model,key, signature, width, height, resourceClass, transcodeClass, priority,diskCacheStrategy, /*磁盘缓存策略*/ transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);

    jobs.put(key, engineJob);

    engineJob.addCallback(cb, callbackExecutor);

    engineJob.start(decodeJob);

    return new LoadStatus(cb, engineJob);

}

从EngineJob缓存列表中查找是否有可重用的EngineJob,如果有直接重用;否则新建一个EngineJob,并开启该JobengineJob.start(decodeJob);

EngineJob.java:

public synchronized void start(DecodeJob<R> decodeJob) {

    this.decodeJob = decodeJob;

    GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();

    executor.execute(decodeJob);

}

解码工作是耗时操作,不能在主线程操作,因此把decodeJob提交到线程池执行:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback, Runnable, Comparable<DecodeJob<?>>, Poolable {

    @Override

    public void run() {

        DataFetcher<?> localFetcher = currentFetcher;

        try {

            if (isCancelled) {

                notifyFailed();

                return;

            }

            runWrapped();

        } catch (CallbackException e) {

            throw e;

        } catch (Throwable t) {// 异常处理

        } finally { //清理工作

        }

   }

}

  private void runWrapped() {

      switch (runReason) {

          case INITIALIZE:

              stage = getNextStage(Stage.INITIALIZE); //计算nextStage

              currentGenerator = getNextGenerator(); //根据nextStage获取对应的Generator

              runGenerators(); //执行runGenerators()

              break;

          case SWITCH_TO_SOURCE_SERVICE:

              runGenerators();

              break;

          case DECODE_DATA:

              decodeFromRetrievedData();

              break;

          default:

              throw new IllegalStateException( "Unrecognized run reason: " + runReason);

        }

  }

  private Stage getNextStage(Stage current) {

      switch (current) {

          case INITIALIZE:

              return diskCacheStrategy.decodeCachedResource()? Stage.RESOURCE_CACHE : getNextStage( Stage.RESOURCE_CACHE);

          case RESOURCE_CACHE:

              return diskCacheStrategy.decodeCachedData()? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);

          case DATA_CACHE:

              return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;

          case SOURCE:

          case FINISHED:

              return Stage.FINISHED;

          default:

              throw new IllegalArgumentException( "Unrecognized stage: " + current);

        }

  }

  private DataFetcherGenerator getNextGenerator() {

      switch (stage) {

          case RESOURCE_CACHE:

              return new ResourceCacheGenerator( decodeHelper, this); //缓存的处理过的资源的生成器

          case DATA_CACHE:

              return new DataCacheGenerator( decodeHelper, this); //缓存的原始资源的生成器

          case SOURCE:

              return new SourceGenerator( decodeHelper, this); //使用modelLoader和model获取原始资源的生成器

          case FINISHED:

              return null;

          default:

              throw new IllegalStateException( "Unrecognized stage: " + stage);

      }

}

Glide的缓存机制简单总结就是:

若允许内存缓存,先从内存LruCache中查找,若存在则将该资源添加到activeResouces弱引用map中然后返回,该map主要用来缓存当前正在使用的资源,若不存在则判断activeResouces弱引用map中是否存在,若存在则返回,每次资源处于正在使用状态,则引用计数加一,若未使用,则缓存到LruCache中。若内存中不存在,若允许磁盘缓存,再从磁盘缓存中查找。若磁盘中不存在,则直接下载,然后再根据磁盘缓存策略缓存原图或是经转换后的图到磁盘中。

 

 

 


http://chatgpt.dhexx.cn/article/IU5iSped.shtml

相关文章

[软件更新]gladder2.0.3.3

介绍 gladder是一个Firefox插件&#xff0c;名字被解释为Great Ladder (Ladder for Great Firewall)&#xff0c;目标是帮助人们跨过Great Firewall访问境外被查封的网站。 安装 https://addons.mozilla.org/en-US/firefox/addon/2864 (点击页面中的Install Now按钮) 功能 * 自…

飞机游戏代码(JAVA)

&#xff2d;yGameFrame类: 主要的调用类 package sc.wh.game;import javax.swing.JFrame; import java.awt.Color; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import sc.wh.game.*; import java.awt.event.KeyAdapte…

基于java的拼图经典游戏(附代码)

拼图游戏是一款经典的益智游戏&#xff0c;游戏开始前图片被随机打乱&#xff0c;空块位于最右下角&#xff0c;玩家通过点击空块周围图片或者按键方式对图片和空块进行相互交换&#xff0c;直到所有图片都回到原位即为游戏胜利。 本次制作的拼图游戏运行界面如下&#xff1a;…

java推箱子游戏源代码_java实现推箱子小游戏(附源码)

先上效果图 可以通过AWSD进行移动和推箱子 自己弄出来的代码玩起来还是很有意思的。 代码一共是三个.java文件,代码内容如下所示 package ss; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.eve…

免费Java游戏源代码素材推荐

家人们&#xff0c;最近我找到了一个很好用的Java游戏源代码免费素材网站 资源贼多&#xff0c;重点是免费&#xff01;&#xff01;&#xff01;白嫖一时爽&#xff0c;一直白嫖一直爽&#xff0c;嘿嘿嘿&#xff01;&#xff01;&#xff01;感兴趣的可以进去看看 接下来就…

java连连看代码_java实现连连看游戏

本文实例为大家分享了java实现连连看游戏的具体代码,供大家参考,具体内容如下 代码会实现共享的,这个是截图 代码: package com.lr.bean; import java.util.Scanner; import java.util.Random; import com.lr.bean.Point; public class Link{public static void main(Strin…

JAVA版扫雷游戏,清晰易懂,注释多

这是一篇关于JAVA的扫雷游戏&#xff0c;所有的图片均用文字代替&#xff0c;代码可直接运行。 文章目录 开发环境一、下载方法二、运行效果展示三、代码部分1.代码如下 总结 开发环境 开发工具&#xff1a;eclipse2021-12 JDK版本&#xff1a;JDK15.0.1 一、下载方法 链接&a…

猜数字游戏(Java源代码)

游戏后台随机生成1-20之间的5个数&#xff0c;未猜中提示“未命中”&#xff0c;继续猜测&#xff0c;猜中提示“运气不错&#xff0c;猜中了”&#xff0c;并输出数据出现的第一次位置源代码&#xff1a; import java.util.Random; import java.util.Scanner;//游戏后台随机…

JAVA实现扫雷游戏

后记&#xff1a;经评论区提醒&#xff0c;发现有两个bug没考虑到&#xff0c;感谢大家的提醒 bug1&#xff1a;绘制雷的时候有可能把两个雷随机到同样的位置。解决方法是在绘制雷的for循环内&#xff0c;rRow和rCol生成后做一个检测即可&#xff1a; /* 绘制地雷 */private v…

Java抽奖小游戏(包含代码)

情景&#xff1a; 假如从50个数字中确定10个中奖号码。 中奖号码要从50个数字中随机产生&#xff0c;中奖号码不可以重复&#xff0c;并对中奖号码进行排序。 解题思路&#xff1a; 首先建立一个长度为n号码的号码库&#xff1a;建立一个数组存放k个中奖号码抽取k个中奖号码。…

贪吃蛇java游戏代码_java实现贪吃蛇游戏代码(附完整源码)

先给大家分享源码&#xff0c;喜欢的朋友点此处下载。 游戏界面 GUI界面 java实现贪吃蛇游戏需要创建一个桌面窗口出来&#xff0c;此时就需要使用java中的swing控件 创建一个新窗口 JFrame frame new JFrame("贪吃蛇游戏"); //设置大小 frame.setBounds(10, 10, 90…

JAVA 实现生命游戏

生命游戏的规则: 生命游戏中&#xff0c;对于任意细胞&#xff1a;每个细胞有两种状态&#xff1a;存活或死亡。每个细胞与以自身为中心的周围八格细胞产生互动。    1.当前细胞为存活状态时&#xff0c;当周围的活细胞低于2个时&#xff0c; 该细胞因孤独而死亡;    2.当…

Java五子棋全代码

用Java编写简单的五子棋 前言 这两天在空闲时间做了个五子棋项目&#xff0c;分享给大家看一下&#xff0c;界面是这样的&#xff1a;        呜呜呜&#xff0c;界面很丑我知道&#xff0c;本人虽有几年PS基础&#xff0c;但知识浅薄&#xff0c;审美观不尽人意&#xff…

五子棋小游戏 java版(代码+详细注释)

游戏展示 这周闲来无事&#xff0c;再来写个五子棋小游戏。基本功能都实现了&#xff0c;包括人人对战、人机对战。界面布局和功能都写的还行&#xff0c;没做到很优秀&#xff0c;但也不算差。如有需要&#xff0c;做个java初学者的课程设计或者自己写着玩玩也都是不错的&…

【Java实现小游戏】飞翔的小鸟(源码)

游戏玩法&#xff1a;通过鼠标点击使小鸟上下移动穿过柱子并完成得分&#xff0c;小鸟碰到柱子或掉落到地面上都会结束游戏。 &#xff08;游戏内图片&#xff09; 下面是实现这个游戏的代码&#xff1a; Brid类&#xff1a; package bird;import org.omg.CORBA.IMP_LIMIT;im…

用简单Java代码尝试在控制台写游戏(附源码)

尝试写了一个在Java控制台运行的代码游戏&#xff0c;由于写这个的时候&#xff0c;博主还没学到可视化界面&#xff0c;也没有学到面向对象&#xff0c;甚至没有集合&#xff0c;运用的全是之前C语言的语法&#xff0c;因此应该很容易看懂吧。末尾附上源码。 以下是效果展示 …

Java小游戏练习---超级玛丽代码实现

B站教学视频&#xff1a; 01_超级玛丽_创建窗口_哔哩哔哩_bilibili 素材提取&#xff1a; 【超级会员V2】我通过百度网盘分享的文件&#xff1a;Java游戏项目… 链接:百度网盘 请输入提取码 提取码:k6j1 复制这段内容打开「百度网盘APP 即可获取」 百度网盘 请输入提取码 百度…

java 300行代码 冒险闯关小游戏(代码+讲解)

作为一个男孩子&#xff0c;从小就喜欢晚一些游戏。今天就用java写一个类似马里奥的冒险闯关小游戏&#xff0c;但这也不是一两天能完成的事情&#xff0c;我将会持续更新这个游戏&#xff08;有什么好的介意也非常欢迎大家提出来&#xff0c;也能在我的基础上自己接着写&#…

宝塔php防盗链,[宝塔面板]如何开启防盗链?

为了节省建站成本&#xff0c;大多数站长都会选择一些小服务器&#xff0c;在另加CDN、云存储之类的来减轻服务器的压力&#xff0c;但是这类功能大多收费。 如果自己网站的图片都让别人复制图片链接地址&#xff0c;那么自己CDN流量就跑得贼快&#xff0c;花了钱得不到好处。 …

HTTP防盗链(Referer)

HTTP请求防盗链&#xff1a;只允许某些域名请求来源才可以访问。比如A网站有一张图片或音频等资源被B网站直接通过img等标签属性引入使用&#xff0c;这样就是B网站盗用了A网站的资源。那么对于A网站来说&#xff0c;流量怎么被消耗的都不知道。 解决思路&#xff1a; 判断http…