Android 开发之Glide《一》

前言

Glide是一个快速高效的Android图片加载库。同样的本篇文章侧重分析Glide的源码分析,不介绍Glide 的使用,有不会用的同学可以先百度一下。

在介绍Glide 之前,我们先分析一个图片库需要解决哪些问题。

  1. 通常情况下我们的图片都是展示在一个列表里面,如果上下滑动的话,需要确保这个图片只会创建一次请求。

  2. 快速滑动列表的时候会android 会复用View,此时怎么避免图片显示错乱,例如ImageView
    当前显示A图片,滑动的时候会多次复用,需要依次显示B,C 图片,需要确保图片显示顺序正确以及最终显示的图片不会错乱。

  3. 当页面销毁之后,下载图片的线程还在运行,需要避免线程持有页面或者ImageView的引用,否者会导致内存泄露。

  4. 手机内存优先,而图片又很大,需要避免内存溢出,同时为了提高效率还需要在本地磁盘缓存图片。这些可以看成是图片缓存问题。

上述是与图片相关的问题,此外还需要考虑其他的问题,

  1. 框架的扩展性

一、Glide的创建

        RequestOptions options = new RequestOptions()
                .placeholder(R.drawable.booth_map)
        Glide.with(mContext).load(url) .apply(options).into(viewHolder.ivPictureText);

1.1 with

  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

with 方法主要是创建一个RequestManager,RequestManager 用于发起一个请求,RequestManager内部包含一个Glide 对象。

  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever();
  }
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }

    return glide;
  }

Glide.get 方法主要是创建一个单例的glide 对象,在创建完成这个对象的同时初始化对象,

  private static void checkAndInitializeGlide(@NonNull Context context) {
    isInitializing = true;
    initializeGlide(context);
    isInitializing = false;
  }
  private static void initializeGlide(@NonNull Context context) {
    initializeGlide(context, new GlideBuilder());
  }

  @SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    //annotationGeneratedModule 是编译时生成的一个类
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
     //创建ManifestParser,parsse 读取manifest 文件,根据反射创建对应的类。
      manifestModules = new ManifestParser(applicationContext).parse();
    }
	//
    if (annotationGeneratedModule != null
        && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
      Set<Class<?>> excludedModuleClasses =
          annotationGeneratedModule.getExcludedModuleClasses();
      Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
      while (iterator.hasNext()) {
        com.bumptech.glide.module.GlideModule current = iterator.next();
        if (!excludedModuleClasses.contains(current.getClass())) {
          continue;
        }
        iterator.remove();
      }
    }


    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //创建glide
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    //配置glide
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
    //调用回调
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

GeneratedAppGlideModule 是编译期间动态生成的一个类,之所以才有编译生成是为了提高效率,这里不分析GeneratedAppGlideModule,我们只看ManifestParser。这两者最终实现的功能是一致的,只是ManifestParser 通过反射创建GlideModule 效率第一点。

GlideModule 可以用来配置Glide ,从上面的源码可以看到这创建完glide 对象之后会调用所有的GlideModule 的registerComponents 方法配置Glide.
在这里插入图片描述
我们看一下ManifestParser 的parser 方法创建GlideModule 的过程

  public List<GlideModule> parse() {
    List<GlideModule> modules = new ArrayList<>();
    try {
      ApplicationInfo appInfo = context.getPackageManager()
          .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
      if (appInfo.metaData == null) {
        return modules;
      }
      for (String key : appInfo.metaData.keySet()) {
        if (“GlideModule”.equals(appInfo.metaData.get(key))) {
          modules.add(parseModule(key));
        }
      }
    } catch (PackageManager.NameNotFoundException e) {
		xx
    }


    return modules;
  }
 private static GlideModule parseModule(String className) {
 //代码有删减
    Class<?> clazz= Class.forName(className);
    Object module = null;

      module = clazz.getDeclaredConstructor().newInstance();
    return (GlideModule) module;
  }

在这里插入图片描述

parse 方法就是读取清单配置中的里面的meta-data 数据。然后根据反射创建一个对象。

看完parse 方法之后我们看看Gilde的创建

   Glide glide = builder.build(applicationContext);

@NonNull
public Glide build(@NonNull Context context) {
//一个线程池,可以加载URL资源或者是本地资源
  if (sourceExecutor == null) {
   //是null的话代表我们自定义的GlideModule没有进行这一项的配置
   //那么Glide会使用系统自定义的线程池类配置。同理下面的配置也是这样。
    sourceExecutor = GlideExecutor.newSourceExecutor();
  }
//加载缓存在磁盘里面的数据,
  if (diskCacheExecutor == null) {
    diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
  }
//动画资源加载线线程池
  if (animationExecutor == null) {
    animationExecutor = GlideExecutor.newAnimationExecutor();
  }

//内内存计算器
  if (memorySizeCalculator == null) {
    memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
  }
//网络状态检测器
  if (connectivityMonitorFactory == null) {
    connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
  }
//bitmap资源缓冲池,
//避免大量创建于回收Bitmap导致内存抖动。
  if (bitmapPool == null) {
    int size = memorySizeCalculator.getBitmapPoolSize();
    if (size > 0) {
      bitmapPool = new LruBitmapPool(size);
    } else {
      bitmapPool = new BitmapPoolAdapter();
    }
  }
//数组资源缓冲池
  if (arrayPool == null) {
    arrayPool = new 
LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
  }
//内存缓冲,实际就是一个LinkedHashMap
  if (memoryCache == null) {
    memoryCache = new 
LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
  }
、、本地磁盘缓存器,默认为存储在app的内部私密目录。
  if (diskCacheFactory == null) {
    diskCacheFactory = new InternalCacheDiskCacheFactory(context);
  }
//穿件图片加载引擎
  if (engine == null) {
    engine =
        new Engine(
            memoryCache,
            diskCacheFactory,
            diskCacheExecutor,
            sourceExecutor,
            GlideExecutor.newUnlimitedSourceExecutor(),
            GlideExecutor.newAnimationExecutor(),
            isActiveResourceRetentionAllowed);
  }
//建立请求索引器,requestManagerFactory 可能是null,
   //若是null,那么RequestManagerRetriever 会使用默认值。
//参见器构造函数
  RequestManagerRetriever requestManagerRetriever =
      new RequestManagerRetriever(requestManagerFactory);

  return new Glide(
      context,
      engine,
      memoryCache,
      bitmapPool,
      arrayPool,
      requestManagerRetriever,
      connectivityMonitorFactory,
      logLevel,
      defaultRequestOptions.lock(),
      defaultTransitionOptions);
}

通过以上一系列工具的新建,Glide建立了资源请求线程池,本地缓存加载线程池,动画线程池,内存缓存器,磁盘缓存工具等等,接着构造了Engine数据加载引擎,最后再将Engine注入Glide,构建Glide。

注意这里的 RequestManagerRetriever 的创建,这个对象正式getRetriever返回的对象
在这里插入图片描述

接下来我们进入RequestManagerRetriever 的get方法,get 方法最终会调用到getApplicationManager方法

  private RequestManager getApplicationManager(@NonNull Context context) {
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }

getApplicationManager这个通过工厂模式创建了一个单例的RequestManager。

到此Glide.with 方法我们就分析完了,整个过程就是创建Glide自己初始化Glide的一个过程。

二、创建请求

        RequestOptions options = new RequestOptions()
                .placeholder(R.drawable.booth_map)
        Glide.with(mContext).load(url) .apply(options).into(viewHolder.ivPictureText);

2.1 解决图片错乱

在这里插入图片描述
在这里插入图片描述

as 方法主要是创建了一个RequestBuilder对象,表示一个请求.创建完RequestBuilder之后紧接着调用了了RequestBuilder的load 方法

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

RequestBuilder的load 方法并没有真正的开始下载图片,此时仅仅是配置这个RequestBuilder。

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

在返回RequestBuilder 之后我们又调用了apply,error 两个方法配置这个RequestBuilder

最后代用into 方法开始下载对应的图片

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
	//删除部分图片转换设置。
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

buildImgeViewTarget 返回的是一个DrawableImageViewTarget ,关于这个方法这里不展开介绍,这里可以看成是对ImageView 的代理

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @NonNull RequestOptions options) {
  Util.assertMainThread();
	//代码有删减
  options = options.autoClone();
 //创建请求
  Request request = buildRequest(target, targetListener, options);

 //获取之前的请求
  Request previous = target.getRequest();
  //判断请求是否已经存在
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    request.recycle();

    if (!Preconditions.checkNotNull(previous).isRunning()) {

      previous.begin();
    }
    return target;
  }

  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);

  return target;
}
  1. (request.isEquivalentTo(previous)
    && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)
    新旧request相同的情况下,且旧request有图片内存缓存配置但是没有加载完成,这时回收掉新request。旧request不空且未正在执行时(暂停状态),重新启动旧request。
    用于避免重复创建请求,例如ImageView 之前显示的图片A,当被复用的时候显示的图片还是A,那么就没有必要新建一个新的请求了。

  2. buildRequest
    这里主要是创建一个请求,请求的创建过程我们放到一会在介绍,我们先看看
    target.getRequest()方法。
    我们知道在滑动列表的时候会View存在复用的情形,又因为图片是从网络加载,图片的到达时间是不确定的,因此可能导致图片错位问题。我们看看Glide是怎么解决这个问题的。

  public Request getRequest() {
    Object tag = getTag();
    Request request = null;
    if (tag != null) {
      if (tag instanceof Request) {
        request = (Request) tag;
      } else {
        throw new IllegalArgumentException(
            "You must not call setTag() on a view Glide is targeting");
      }
    }
    return request;
  }
  private Object getTag() {
    if (tagId == null) {
      return view.getTag();
    } else {
      return view.getTag(tagId);
    }
  }

view 就是显示图片的ImageView,Glide在请求的时候通过View 的tag 保存最近的一次请求,当滑动的时候这个View被复用并且请求一个显示一个新的图片,此时先获去tag查看View 是不是已经存在一个请求了,如果存在就比较这两个请求是不是一致,如果一致那么忽略这次请求,如果不一致,那么保存最新的额请求,并且清除就的请求。
在这里插入图片描述

  public void setRequest(@Nullable Request request) {
    setTag(request);
  }
  private void setTag(@Nullable Object tag) {
    if (tagId == null) {
      isTagUsedAtLeastOnce = true;
      view.setTag(tag);
    } else {
      view.setTag(tagId, tag);
    }
  }

在setRequest 之前先调用了 requestManager.clear(target);clear 主要是删除旧的请求成功之后的回调,也就是旧的 回来之后只需要把图片缓存起来,而不需要显示这个图片。

  public void clear(@Nullable final Target<?> target) {
  		//代码有删减
		    untrackOrDelegate(target);
  }
  private void untrackOrDelegate(@NonNull Target<?> target) {
  //这是重点
    boolean isOwnedByUs = untrack(target);
    if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
      Request request = target.getRequest();
      //清除tag
      target.setRequest(null);
      request.clear();
    }
  }

这里主要是调用了untrack 方法

  boolean untrack(@NonNull Target<?> target) {
    Request request = target.getRequest();
    // If the Target doesn't have a request, it's already been cleared.
    if (request == null) {
      return true;
    }

    if (requestTracker.clearRemoveAndRecycle(request)) {
      targetTracker.untrack(target);
      target.setRequest(null);
      return true;
    } else {
      return false;
    }
  }

clearRemoveAndRecycle 方法内部会调用Request 的clear 与recycle 方法 ,
在这里插入图片描述

recycle 方法会清除很多资源,例如设置target=null ,也就是释放ImageView 的引用。避免内存泄露。
clear 内部有调用了cancel 方法
SingleRequest.java

  void cancel() {
   	//移出回调
    target.removeCallback(this);
    status = Status.CANCELLED;
    if (loadStatus != null) {
      loadStatus.cancel();
      loadStatus = null;
    }
  }

target.removeCallback(this); 中的target 是前面提到的DrawableImageViewTarget,this 是SImpleRequest,其实现了ResourceCallback接口,当将图片下载下来之后会执行这个接口的onResourceReady 方法
在这里插入图片描述

  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    loadStatus = null;
   //代码有删减。resource 就是下载下来的图片
    Object received = resource.get();
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }
  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // 代码有删减
	  target.onResourceReady(result, animation);

  }

target.onResourceReady 内会调用到setResource 方法显示图片。

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

由上可知clear 方法最终会删除请求回调方法,因此当下载完成图片之后不会执行显示图片的回调,进而避免了显示错乱的问题。

2.1 创建请求

下面我们来分析一些请求的创建过程也就是buildRequest 方法

  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    options = options.autoClone();
    //创建请求
    Request request = buildRequest(target, targetListener, options);
    //代码有删减

    Request previous = target.getRequest();


    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }
  private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      RequestOptions requestOptions) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions);
  }

buildRequest取了requestOptions的一些信息,以及transitionOptions信息,继续往下调用buildRequestRecursive。

private Request buildRequestRecursive(
    Target<TranscodeType> target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @Nullable RequestCoordinator parentCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    RequestOptions requestOptions) {

  // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
  ErrorRequestCoordinator errorRequestCoordinator = null;
//创建协同类
  if (errorBuilder != null) {
    errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
    parentCoordinator = errorRequestCoordinator;
  }

  Request mainRequest =
      buildThumbnailRequestRecursive(
          target,
          targetListener,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          requestOptions);

  if (errorRequestCoordinator == null) {
    return mainRequest;
  }

//创建错误请求
  int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
  int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
  if (Util.isValidDimensions(overrideWidth, overrideHeight)
      && !errorBuilder.requestOptions.isValidOverride()) {
    errorOverrideWidth = requestOptions.getOverrideWidth();
    errorOverrideHeight = requestOptions.getOverrideHeight();
  }

  Request errorRequest = errorBuilder.buildRequestRecursive(
      target,
      targetListener,
      errorRequestCoordinator,
      errorBuilder.transitionOptions,
      errorBuilder.requestOptions.getPriority(),
      errorOverrideWidth,
      errorOverrideHeight,
      errorBuilder.requestOptions);
 //包裹着两个Request,实现类似代理的功能
  errorRequestCoordinator.setRequests(mainRequest, errorRequest);
  return errorRequestCoordinator;
}

buildRequestRecursive()这里主要涉及两个Request,mainRequest和errorRequest,大概的意思是没有errorRequest的时候直接用mainRequest,否则将这两个request包在ErrorRequestCoordinator中,ErrorRequestCoordinator也是一个Request的子类,起到代理的作用。

buildRequestRecursive方法生成了两个Request:mainRequest、errorRequest,errorRequest的生成使用了递归

只有在手动设置errorBuilder的情况下才会触发生成errorRequest的逻辑,当mainRequest加载失败时ErrorRequestCoordinator就会启动errorRequest执行。

继续往下看mainRequest是如何生成的,buildThumbnailRequestRecursive()处理的事情与buildRequestRecursive()类似,如果我们设置了缩略图那么会穿件一个缩略图请求然后创建一个正常的请求感性的的同学可以自己看一下这个方法,或者是参阅下面这篇文章。
Glide缩略图

而errorBuilder.buildRequestRecursive()处理的是错误的情况。整个Request 最后会形成一个树状结构如下

在这里插入图片描述
一般情况下会形成这样一个requests树,此时缩略图与errRequest 都是一个简单的网络请求图,但是实际上缩略图与错误图片 本身可能也会请求错误后者缩略图,这个时候就会继续往下嵌套,这是一个递归的过程。在这里插入图片描述
但是不论递归嵌套多少次最终的叶子节点一定是是SingleRequest,SingleRequest 可以看成是一个对图片的网络请求。

创建SingleRequest 的方法是obtainRequest()

private Request obtainRequest(
    Target<TranscodeType> target,
    RequestListener<TranscodeType> targetListener,
    RequestOptions requestOptions,
    RequestCoordinator requestCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight) {
  return SingleRequest.obtain(
      context,
      glideContext,
      model,
      transcodeClass,
      requestOptions,
      overrideWidth,
      overrideHeight,
      priority,
      target,
      targetListener,
      requestListener,
      requestCoordinator,
      glideContext.getEngine(),
      transitionOptions.getTransitionFactory());
}

看到这个方法的名字,有没有觉得很熟悉,对,我们的Handler里面Message就有类似的方法,这里Glide用到了享元的一种设计模式,出于对内存的节省。接下来继续分析obtain的实现。

public static <R> SingleRequest<R> obtain(
    Context context,
    GlideContext glideContext,
    Object model,
    Class<R> transcodeClass,
    RequestOptions requestOptions,
    int overrideWidth,
    int overrideHeight,
    Priority priority,
    Target<R> target,
    RequestListener<R> targetListener,
    RequestListener<R> requestListener,
    RequestCoordinator requestCoordinator,
    Engine engine,
    TransitionFactory<? super R> animationFactory) {
   //有则回获取,避免重复创建 
 SingleRequest<R> request =
      (SingleRequest<R>) POOL.acquire();
  //没有则重新创建
  if (request == null) {
    request = new SingleRequest<>();
  }
 //初始化这个Request
  request.init(
      context,
      glideContext,
      model,
      transcodeClass,
      requestOptions,
      overrideWidth,
      overrideHeight,
      priority,
      target,
      targetListener,
      requestListener,
      requestCoordinator,
      engine,
      animationFactory);
  return request;
}

POOL 可以看成是一个列表,关于这个对象这里就不往下分析了。
可以看到,先是从对象池里面去取,有则共享,减少new对象的成本。然后调用init方法,进行一些参数设置。最后我们看到,一个request对象的创建也就结束了。创建完成请求之后,下一步就是正式下载对应的图片,关于这一部分内容,我们在下一篇来介绍。

总结

本篇主要是介绍了Glide的创建过程以及Request的创建过程,同时在Request的创建过程中学习了Glide是怎样避免内存泄露以及图片错乱的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值