Android源码进阶之Glide加载流程和源码详解

移动开发 Android
Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式; 昨天我们从源码里分析了,glide的缓存策略机制;那今天我们就趁热打铁来分析一波加载流程。

[[421043]]

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式;

昨天我们从源码里分析了,glide的缓存策略机制;

那今天我们就趁热打铁来分析一波加载流程;

一、glide常用的加载方法

1、加载图片到imageView

  1. Glide.with(Context context).load(Strint url).into(ImageView imageView); 

2、各种形式的图片加载到ImageView

  1. // 加载本地图片 
  2. File file = new File(getExternalCacheDir() + "/image.jpg"); 
  3. Glide.with(this).load(file).into(imageView); 
  4. // 加载应用资源 
  5. int resource = R.drawable.image; 
  6. Glide.with(this).load(resource).into(imageView); 
  7. // 加载二进制流 
  8. byte[] image = getImageBytes(); 
  9. Glide.with(this).load(image).into(imageView); 
  10. // 加载Uri对象 
  11. Uri imageUri = getImageUri(); 
  12. Glide.with(this).load(imageUri).into(imageView); 

3、加载带有占位图

  1. Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView); 

占位图目的为在目的图片还未加载出来的时候,提前展示给用户的一张图片;

4、加载失败 放置占位符

  1. Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error) 
  2.      .diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制 
  3.      .into(imageView); 
  4. //DiskCacheStrategy.NONE:表示不缓存任何内容。 
  5. //DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
  6. //DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
  7. //DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 

5、加载指定格式的图片--指定为静止图片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。 
  4.      .placeholder(R.drawable.loading) 
  5.      .error(R.drawable.error) 
  6.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  7.      .into(imageView); 

6、加载动态图片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。 
  4.      .placeholder(R.drawable.loading) 
  5.      .error(R.drawable.error) 
  6.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  7.      .into(imageView); 

7、加载指定大小的图片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .placeholder(R.drawable.loading) 
  4.      .error(R.drawable.error) 
  5.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  6.      .override(100, 100)//指定图片大小 
  7.      .into(imageView); 

8、关闭框架的内存缓存机制

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .skipMemoryCache(true)  //传入参数为false时,则关闭内存缓存。 
  4.      .into(imageView); 

9、关闭硬盘的缓存

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .diskCacheStrategy(DiskCacheStrategy.NONE)     //关闭硬盘缓存操作 
  4.      .into(imageView); 
  5. //其他参数表示: 
  6. //DiskCacheStrategy.NONE:表示不缓存任何内容。 
  7. //DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
  8. //DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
  9. //DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 

10、当引用的 url 存在 token 时解决方法

  1. public class MyGlideUrl extends GlideUrl { 
  2.     private String mUrl; 
  3.     public MyGlideUrl(String url) { 
  4.         super(url); 
  5.         mUrl = url; 
  6.     } 
  7.     @Override 
  8.     public String getCacheKey() { 
  9.         return mUrl.replace(findTokenParam(), ""); 
  10.     } 
  11.     private String findTokenParam() { 
  12.         String tokenParam = ""
  13.         int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token="); 
  14.         if (tokenKeyIndex != -1) { 
  15.             int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1); 
  16.             if (nextAndIndex != -1) { 
  17.                 tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1); 
  18.             } else { 
  19.                 tokenParam = mUrl.substring(tokenKeyIndex); 
  20.             } 
  21.         } 
  22.         return tokenParam; 
  23.     } 

然后加载图片的方式为:

  1. Glide.with(this) 
  2.      .load(new MyGlideUrl(url)) 
  3.      .into(imageView); 

11、利用Glide将图片加载到不同控件或加载成不同使用方式

(1)、拿到图片实例

  1. //1、通过自己构造 target 可以获取到图片实例 
  2. SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() { 
  3.     @Override 
  4.     public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
  5.         imageView.setImageDrawable(resource); 
  6.     } 
  7. }; 
  8. //2、将图片实例记载到指定的imageview上,也可以做其他的事情 
  9. public void loadImage(View view) { 
  10.     String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
  11.     Glide.with(this) 
  12.          .load(url) 
  13.          .into(simpleTarget); 

(2)、将图片加载到任何位置

  1. /* 
  2. *将图片加载为控件背景 
  3. */ 
  4. public class MyLayout extends LinearLayout { 
  5.     private ViewTarget<MyLayout, GlideDrawable> viewTarget; 
  6.     public MyLayout(Context context, AttributeSet attrs) { 
  7.         super(context, attrs); 
  8.         viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) { 
  9.             @Override 
  10.             public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
  11.                 MyLayout myLayout = getView(); 
  12.                 myLayout.setImageAsBackground(resource); 
  13.             } 
  14.         }; 
  15.     } 
  16.     public ViewTarget<MyLayout, GlideDrawable> getTarget() { 
  17.         return viewTarget; 
  18.     } 
  19.     public void setImageAsBackground(GlideDrawable resource) { 
  20.         setBackground(resource); 
  21.     } 
  22. //引用图片到指定控件作为背景 
  23. public class MainActivity extends AppCompatActivity { 
  24.     MyLayout myLayout; 
  25.     @Override 
  26.     protected void onCreate(Bundle savedInstanceState) { 
  27.         super.onCreate(savedInstanceState); 
  28.         setContentView(R.layout.activity_main); 
  29.         myLayout = (MyLayout) findViewById(R.id.background); 
  30.     } 
  31.     public void loadImage(View view) { 
  32.         String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
  33.         Glide.with(this) 
  34.              .load(url) 
  35.              .into(myLayout.getTarget()); 
  36.     } 

12、Glide 实现预加载

  1. //a、预加载代码 
  2. Glide.with(this) 
  3.      .load(url) 
  4.      .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
  5.      .preload(); 
  6. //preload() 有两种重载 
  7.  // 1、带有参数的重载,参数作用是设置预加载的图片大小; 
  8. //2、不带参数的表示加载的图片为原始尺寸; 
  9. //b、使用预加载的图片 
  10. Glide.with(this) 
  11.      .load(url) 
  12.      .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
  13.      .into(imageView); 

二、Glide加载流程详解

1、with(context)

  1. // Glide.java 
  2. public static RequestManager with(@NonNull Context context) { 
  3.     return getRetriever(context).get(context); 
  4. public static RequestManager with(@NonNull Activity activity) { 
  5.     return getRetriever(activity).get(activity); 
  6. public static RequestManager with(@NonNull FragmentActivity activity) { 
  7.     return getRetriever(activity).get(activity); 
  8. public static RequestManager with(@NonNull Fragment fragment) { 
  9.     return getRetriever(fragment.getContext()).get(fragment); 
  10. public static RequestManager with(@NonNull View view) { 
  11.     return getRetriever(view.getContext()).get(view); 

该函数创建了Glide实例并初始化了一些基本参数,然后创建了一个RequestManager对象并返回。总共有5个场景,这里就先选取参数为Context类型情形进行分析。

  1. // Glide.java 
  2. public static RequestManager with(@NonNull Context context) { 
  3.     return getRetriever(context).get(context); 

可以看到该函数首先调用了getRetriever(context)获取到了RequestManagerRetriever对象。在创建该对象之前首先通过Glide.java中的get方法获得了Glide实例(Glide是一个单例),同时读取AppGlideModule和AndroidManifest.xml的配置。

  1. // Glide.java 
  2. private static RequestManagerRetriever getRetriever(@Nullable Context context) { 
  3.     // Glide.get(context)获取Glide实例 
  4.     return Glide.get(context).getRequestManagerRetriever(); 
  5. public static Glide get(@NonNull Context context) { 
  6.     if (glide == null) { 
  7.       // 加载AppGlideModule 
  8.       GeneratedAppGlideModule annotationGeneratedModule = 
  9.           getAnnotationGeneratedGlideModules(context.getApplicationContext()); 
  10.       synchronized (Glide.class) { 
  11.         if (glide == null) { 
  12.           // 加载Mainfest配置、注册模块回调 
  13.           // 这一步执行了 Glide.build()方法构造Glide实例。build方法下面会讲到 
  14.           checkAndInitializeGlide(context, annotationGeneratedModule); 
  15.         } 
  16.       } 
  17.     } 
  18.     return glide; 
  19.   } 

获取到Glide实例后,紧接着调用getRequestManagerRetriever方法返回了上一步已经初始化好的RequestManagerRetriever对象。

  1. // Glide.java 
  2.   public RequestManagerRetriever getRequestManagerRetriever() { 
  3.     return requestManagerRetriever; 
  4.   } 

接着再看一看RequestManagerRetriever是如何被初始化的,以及初始化过程中都干了哪些事。首先贴源码看看Glide.build方法内部具体实现(该方法在上述checkAndInitializeGlide()函数中被调用):

  1. // GlideBuilder.java 
  2. Glide build(@NonNull Context context) { 
  3.       // 分配线程池、配置缓存策略 
  4.       sourceExecutor = GlideExecutor.newSourceExecutor(); 
  5.       diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); 
  6.       animationExecutor = GlideExecutor.newAnimationExecutor(); 
  7.       memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); 
  8.       // 监听网络变化 
  9.       connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); 
  10.       int size = memorySizeCalculator.getBitmapPoolSize(); 
  11.       if (size > 0) { 
  12.         bitmapPool = new LruBitmapPool(size); 
  13.       } else { 
  14.         bitmapPool = new BitmapPoolAdapter(); 
  15.       } 
  16.       arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); 
  17.       memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 
  18.       diskCacheFactory = new InternalCacheDiskCacheFactory(context); 
  19.     // engine是负责执行加载任务的 
  20.     if (engine == null) { 
  21.       engine = 
  22.           new Engine( 
  23.               memoryCache, 
  24.               diskCacheFactory, 
  25.               diskCacheExecutor, 
  26.               sourceExecutor, 
  27.               GlideExecutor.newUnlimitedSourceExecutor(), 
  28.               animationExecutor, 
  29.               isActiveResourceRetentionAllowed); 
  30.     } 
  31.     if (defaultRequestListeners == null) { 
  32.       defaultRequestListeners = Collections.emptyList(); 
  33.     } else { 
  34.       defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); 
  35.     } 
  36.     RequestManagerRetriever requestManagerRetriever = 
  37.         new RequestManagerRetriever(requestManagerFactory); 
  38.     return new Glide( 
  39.         context, 
  40.         engine, 
  41.         memoryCache, 
  42.         bitmapPool, 
  43.         arrayPool, 
  44.         requestManagerRetriever, 
  45.         connectivityMonitorFactory, 
  46.         logLevel, 
  47.         defaultRequestOptionsFactory, 
  48.         defaultTransitionOptions, 
  49.         defaultRequestListeners, 
  50.         isLoggingRequestOriginsEnabled, 
  51.         isImageDecoderEnabledForBitmaps); 
  52.   } 
  • 执行Glide.get()方法时就已经分配好了资源加载、缓存线程池、配置好了缓存策略,这里的engine专门负责加载、解码资源,ConnectivityMonitor注册了网络状态监听器,当网络断开时暂停请求网络资源,重连后继续请求资源;
  • RequestManagerRetriever是原来是通过RequestManagerFactory工厂类构造的。进入到RequestManagerFactory.java类中,可以看到get方法获取到了相应的RequestManager对象;
  • 从这里我们可以发现,无论哪种情况,当App进入后台后会导致页面不可见,此时RequestManager绑定到了ApplicationContext,与App的生命周期一致,因此在RequestManager.java类中也实现了生命周期相关的回调函数;
  1. // RequestManagerRetriever.java 
  2. // get有好几个重载方法,这里仅选取context参数进行分析 
  3. public RequestManager get(@NonNull Context context) { 
  4.     if (Util.isOnMainThread() && !(context instanceof Application)) { 
  5.       if (context instanceof FragmentActivity) { 
  6.         return get((FragmentActivity) context); 
  7.       } else if (context instanceof Activity) { 
  8.         return get((Activity) context); 
  9.       } else if (context instanceof ContextWrapper 
  10.           && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { 
  11.         return get(((ContextWrapper) context).getBaseContext()); 
  12.       } 
  13.     } 
  14.     return getApplicationManager(context); 
  15.   } 

执行完Glide.with(context)后我们拿到了一个对应的RequestManager对象,接下来就执行下一个任务load(url);

2、load(url)

拿到了RequestManager,紧接着调用load方法开始执行下一步操作,同样先看看load方法的实现

  1. // RequestManager.java 
  2. public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) { 
  3.     return asDrawable().load(bitmap); 
  4.   } 
  5.   public RequestBuilder<Drawable> load(@Nullable Drawable drawable) { 
  6.     return asDrawable().load(drawable); 
  7.   } 
  8.   public RequestBuilder<Drawable> load(@Nullable String string) { 
  9.     return asDrawable().load(string); 
  10.   } 
  11.   public RequestBuilder<Drawable> load(@Nullable Uri uri) { 
  12.     return asDrawable().load(uri); 
  13.   } 
  14.   public RequestBuilder<Drawable> load(@Nullable File file) { 
  15.     return asDrawable().load(file); 
  16.   } 
  17.   public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) { 
  18.     return asDrawable().load(resourceId); 
  19.   } 
  20.   public RequestBuilder<Drawable> load(@Nullable URL url) { 
  21.     return asDrawable().load(url); 
  22.   } 
  23.   public RequestBuilder<Drawable> load(@Nullable byte[] model) { 
  24.     return asDrawable().load(model); 
  25.   } 
  26.   public RequestBuilder<Drawable> load(@Nullable Object model) { 
  27.     return asDrawable().load(model); 
  28.   } 
  • load()同样有多个重载函数,传入的参数可以是图片对象Bitmap、Drawable、本地资源Uri、在线资源路径Url、文件对象File、assets资源的id,这里我们只看参数为Url的情形;
  • asDrawable().load(url)返回了一个RequestBuilder对象,首先看看asDrawable方法干了什么;
  1. // RequestManager.java 
  2.   public RequestBuilder<Drawable> asDrawable() { 
  3.     return as(Drawable.class); 
  4.   } 
  5.   public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) { 
  6.     return new RequestBuilder<>(glide, this, resourceClass, context); 
  7.   } 

asDrawable方法创建了RequestBuilder对象,然后调用RequestBuilder.java中的load方法;

  1. // RequestBuilder.java 
  2.   // 传入的String类型的url将会被作为缓存的key 
  3.   public RequestBuilder<TranscodeType> load(@Nullable String string) { 
  4.     return loadGeneric(string); 
  5.   } 
  6.   // 这里返回了自身 
  7.   private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { 
  8.     this.model = model; 
  9.     isModelSet = true
  10.     return this; 
  11.   } 

load函数主要工作就是根据传入的资源类型,构造了一个相应的RequestBuilder对象。至此一切准备工作准备就绪,接下来就是最为重要的一步了-加载、展示文件,让我们来着看into(view)方法如何完成这些任务;

3、into(view)

拿到的是对应类型RequestBuilder实例,那么就看看该类里into方法的具体实现。同样into方法有into(@NonNull Y target)和into(@NonNull ImageView )两个重载函数(这两个函数最终都会走到同一个函数中),由于调用into方法时我们传入的参数是ImageView类型的;

  1. // RequestBuilder.java 
  2.   public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { 
  3.     Util.assertMainThread(); 
  4.     BaseRequestOptions<?> requestOptions = this; 
  5.     // View's scale type. 
  6.     // 处理图片缩放,根据缩放类型来初始化对应的requestOptions对象 
  7.     ...... 
  8.     return into
  9.         glideContext.buildImageViewTarget(view, transcodeClass), 
  10.         /*targetListener=*/ null
  11.         requestOptions, 
  12.         Executors.mainThreadExecutor() // 运行在主线程的handler 
  13.     ); 
  14.   } 
  • 上面代码段首先处理图片缩放类型(裁剪、对齐方式等),并将生成的相关参数放入了requestOptions对象中,然后再将其作为参数传给了RequestBuilder.java类私有方法into。该方法定义的四个参数分别为:viewTarget、target回调监听器、请求参数、主线程的回调函数;
  • 显然外部传入ImageView对象最终被转换成了ViewTarget对象,转换函数便是glideContext.buildImageViewTarget(view, transcodeClass);
  1. // GlideContext.java 
  2.   public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) { 
  3.     return imageViewTargetFactory.buildTarget(imageView, transcodeClass); 
  4.   } 

ViewTarget又是由ImageViewTargetFactory工厂方法生成,接着再看buildTarget方法是如何生成ViewTarget对象。

  1. // imageViewTargetFactory.java 
  2. public class ImageViewTargetFactory { 
  3.   public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) { 
  4.     if (Bitmap.class.equals(clazz)) { 
  5.       return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); 
  6.     } else if (Drawable.class.isAssignableFrom(clazz)) { 
  7.       return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view); 
  8.     } else { 
  9.       throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as(Class).transcode(ResourceTranscoder)"); 
  10.     } 
  11.   } 

可以看到无论传入参数是何种类型,最终都会转换成两种类型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;这里如何选择取决于asBitmap()、asGif()和asDrawable()函数是否被调用,默认是Bitmap类型,所以这里默认返回的是BitmapImageViewTarget;

  1. // BitmapImageViewTarget.java 
  2. public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> { 
  3.   public BitmapImageViewTarget(ImageView view) { 
  4.     super(view); 
  5.   } 
  6.   @Override 
  7.   protected void setResource(Bitmap resource) { 
  8.     view.setImageBitmap(resource); // 显示图片 
  9.   } 

至此ViewTarget创建完毕,我们再回到RequestBuilder.java私有into方法

  1. // RequestBuilder.java` 
  2.  private <Y extends Target<TranscodeType>> Y into
  3.       @NonNull Y target, 
  4.       @Nullable RequestListener<TranscodeType> targetListener, 
  5.       BaseRequestOptions<?> options, 
  6.       Executor callbackExecutor) { 
  7.     // 注释1:创建request 
  8.     Request request = buildRequest(target, targetListener, options, callbackExecutor); 
  9.     // 获取前一个reqeust请求对象 
  10.     Request previous = target.getRequest(); 
  11.     // 与上一个请求相同 并且 上一个请求已完成 
  12.     if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
  13.      // 上一个请求已完成,那么重新启动它 
  14.       if (!Preconditions.checkNotNull(previous).isRunning()) { 
  15.         previous.begin(); 
  16.       } 
  17.       return target; 
  18.     } 
  19.     // 与上一个请求不同,则清除掉上一个,再将加入新请求 
  20.     requestManager.clear(target); 
  21.     target.setRequest(request); 
  22.     requestManager.track(target, request); 
  23.     return target; 
  24.   } 

顺着代码次序,来看看这个方法每一步都干了什么:

  • 首先执行buildRequest方法创建一个新的Request请求req1;
  • 获取当前ViewTarget上正在进行中的Request请求req2;
  • 判断新建的请求req1与已有的请求req2是否相同,如果相同则判断是否跳过req2请求的缓存,两个条件都满足则开始执行begin()方法开始请求资源并停止往下执行,条件都不满足则继续执行第四步;
  • 给ViewTarget设置最新的请求req1,然后执行track方法追踪req1。
  • 执行into(view)方法首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();
  1. // ReqeustManager.java   
  2. synchronized void track(@NonNull Target<?> target, @NonNull Request request) { 
  3.     // 与lifecycle绑定 
  4.     targetTracker.track(target); 
  5.     // 启动reqeust 
  6.     requestTracker.runRequest(request); 
  7.   } 
  8. // RequestTracker.java 
  9.   public void runRequest(@NonNull Request request) { 
  10.     requests.add(request); 
  11.     if (!isPaused) { 
  12.       request.begin(); // 立即开始加载 
  13.     } else { 
  14.       //防止从以前的请求中加载任何位图,释放该请求所拥有的任何资源,显示当前占位符(如果提供了该占位符),并将该请求标记为已取消。 
  15.       // request.java( Interface ) 
  16.       request.clear(); 
  17.       pendingRequests.add(request); // 加入队列等待执行 
  18.     } 
  19.   } 
  • track方法的源码,先是执行targetTracker.track(target)监听ViewTarget的请求,然后runRequest开始执行。由于最终都是通过begin()方法开始请求,所以我们先来看看begin()方法的具体实现;
  • Request类是interface类型,begin()它的抽象方法,所以我们要想弄清楚begin()的具体实现,那就要先找到Request的实现类,从buildRequest(xx)方法入手,同样先贴出源码:
  1. // RequestBuilder.java 
  2. private Request buildRequest( 
  3.       Target<TranscodeType> target, 
  4.       @Nullable RequestListener<TranscodeType> targetListener, 
  5.       BaseRequestOptions<?> requestOptions, 
  6.       Executor callbackExecutor) { 
  7.     return buildRequestRecursive( 
  8.         /*requestLock=*/ new Object(), 
  9.         target, 
  10.         targetListener, 
  11.         /*parentCoordinator=*/ null
  12.         transitionOptions, 
  13.         requestOptions.getPriority(), 
  14.         requestOptions.getOverrideWidth(), 
  15.         requestOptions.getOverrideHeight(), 
  16.         requestOptions, 
  17.         callbackExecutor); 
  18.   } 
  19. private Request buildRequestRecursive( 
  20.       Object requestLock, 
  21.       Target<TranscodeType> target, 
  22.       @Nullable RequestListener<TranscodeType> targetListener, 
  23.       @Nullable RequestCoordinator parentCoordinator, 
  24.       TransitionOptions<?, ? super TranscodeType> transitionOptions, 
  25.       Priority priority, 
  26.       int overrideWidth, 
  27.       int overrideHeight, 
  28.       BaseRequestOptions<?> requestOptions, 
  29.       Executor callbackExecutor) { 
  30.     // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator. 
  31.     ErrorRequestCoordinator errorRequestCoordinator = null
  32.     // 请求出错了 
  33.     if (errorBuilder != null) { 
  34.       errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator); 
  35.       parentCoordinator = errorRequestCoordinator; 
  36.     } 
  37.     // 无法确认完成请求和缩略图请求哪个先完成,所以当缩略图比完成请求后完成时就不再显示缩略图 
  38.     Request mainRequest = 
  39.         buildThumbnailRequestRecursive( 
  40.             requestLock, 
  41.             target, 
  42.             targetListener, 
  43.             parentCoordinator, 
  44.             transitionOptions, 
  45.             priority, 
  46.             overrideWidth, 
  47.             overrideHeight, 
  48.             requestOptions, 
  49.             callbackExecutor); 
  50.     // 请求成功了,直接返回缩略图Request 
  51.     if (errorRequestCoordinator == null) { 
  52.       return mainRequest; 
  53.     } 
  54.     // ... 
  55.     Request errorRequest = 
  56.         errorBuilder.buildRequestRecursive( 
  57.             requestLock, 
  58.             target, 
  59.             targetListener, 
  60.             errorRequestCoordinator, 
  61.             errorBuilder.transitionOptions, 
  62.             errorBuilder.getPriority(), 
  63.             errorOverrideWidth, 
  64.             errorOverrideHeight, 
  65.             errorBuilder, 
  66.             callbackExecutor); 
  67.     // 同时返回缩略图请求和错误请求 
  68.     errorRequestCoordinator.setRequests(mainRequest, errorRequest); 
  69.     return errorRequestCoordinator; 
  70.   } 

显然代码里的mainRequest就是我们要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其内部我们发现Request最终其实是由SingleRequest.obtain方法产生,也就是说我们最终拿到的Request其实就是SingleReqeust类的一个实例。这里过程比较简单,代码就不贴出来了。我们直接去SingleReqeust类里面 看看begin方法如何实现的;

  1. // SingleReqeust.java 
  2. public void begin() { 
  3.       if (status == Status.COMPLETE) { 
  4.         // 资源已下载,直接回调 
  5.         // 执行动画 
  6.         onResourceReady(resource, DataSource.MEMORY_CACHE); 
  7.         return
  8.       } 
  9.         // 计算尺寸 
  10.       if (Util.isValidDimensions(overrideWidth, overrideHeight)) { 
  11.         onSizeReady(overrideWidth, overrideHeight); 
  12.       } else { 
  13.         target.getSize(this); 
  14.       } 
  15.       if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) 
  16.           && canNotifyStatusChanged()) { 
  17.         // 开始加载 
  18.         // 设置占位度 
  19.         target.onLoadStarted(getPlaceholderDrawable()); 
  20.       } 
  21.   } 

进入begin方法后首先判断如果资源已经过加载好了则直接回调onResourceReady显示图片并缓存,否则测量出图片尺寸后再开始加载图片(onSizeReady()中执行加载任务)并同时显示占位图:

①overrideWith、overrideHeight通过override(width, height)设置:

Glide.with(mContext).load(url).override(75, 75).into(imageView);

②占位图是用户调用placeholder(resId)设置:

Glide.with(mContext).load(url).placeholder(resId).into(imageView);

接着再看onSizeReady()测量完图片尺寸后如何加载图片的:

  1. // SingleRequest.java 
  2. @Override 
  3.   public void onSizeReady(int width, int height) { 
  4.       if (status != Status.WAITING_FOR_SIZE) { 
  5.         return
  6.       } 
  7.       status = Status.RUNNING; 
  8.       // 获取图片尺寸 
  9.       float sizeMultiplier = requestOptions.getSizeMultiplier(); 
  10.       this.width = maybeApplySizeMultiplier(width, sizeMultiplier); 
  11.       this.height = maybeApplySizeMultiplier(height, sizeMultiplier); 
  12.       // 开始加载任务 
  13.       loadStatus = 
  14.           engine.load
  15.               glideContext, 
  16.               model, 
  17.               requestOptions.getSignature(), 
  18.               this.width, 
  19.               this.height, 
  20.               requestOptions.getResourceClass(), 
  21.               transcodeClass, 
  22.               priority, 
  23.               requestOptions.getDiskCacheStrategy(), 
  24.               requestOptions.getTransformations(), 
  25.               requestOptions.isTransformationRequired(), 
  26.               requestOptions.isScaleOnlyOrNoTransform(), 
  27.               requestOptions.getOptions(), 
  28.               requestOptions.isMemoryCacheable(), 
  29.               requestOptions.getUseUnlimitedSourceGeneratorsPool(), 
  30.               requestOptions.getUseAnimationPool(), 
  31.               requestOptions.getOnlyRetrieveFromCache(), 
  32.               this, 
  33.               callbackExecutor); 
  34.   } 
  • 可以看到真正的下载任务是在Engine类的load方法中实现的,其中也涉及到了图片缓存逻辑;
  • 最终通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上;

总结

Glide初始化、显示占位图、图片封面的整个业务流程都走完了;

可以从中学习glide中的设计模式:单例模式、工厂方法、策略模式等等,发现自己的不足之处;

 

Glide的图片缓存可以看上一篇文章

 

责任编辑:武晓燕 来源: Android开发编程
相关推荐

2021-09-01 06:48:16

AndroidGlide缓存

2021-08-10 20:41:33

AndroidApp流程

2021-09-03 07:27:38

AndroidGlide管理

2021-08-17 13:41:11

AndroidView事件

2021-09-07 06:40:25

AndroidLiveData原理

2021-08-05 20:39:34

AndroidKotlinStandard.kt

2021-09-12 07:30:10

配置

2021-09-30 07:36:51

AndroidViewDraw

2021-09-09 06:55:43

AndroidViewDragHel原理

2015-03-31 18:26:43

陌陌社交

2021-09-08 06:51:52

AndroidRetrofit原理

2021-10-15 09:19:17

AndroidSharedPrefe分析源码

2021-08-23 06:27:46

AndroidctivitysetContentV

2021-09-05 07:35:58

lifecycleAndroid组件原理

2021-08-12 16:28:10

AndroidHandleLooper

2021-09-13 15:17:52

FastThreadL源码Java

2021-08-24 07:53:28

AndroidActivity生命周期

2009-11-30 16:38:30

Android

2011-06-23 13:10:39

Python 对象机制

2014-04-29 13:16:42

OpenGLAndroid库加载过程
点赞
收藏

51CTO技术栈公众号