美文网首页图片
Glide源码解析

Glide源码解析

作者: A邱凌 | 来源:发表于2020-08-18 18:07 被阅读0次

    问题

    • glide是如何进行缓存的?
    • glide是如何多线程加载图片?
    • glide如何进行内存管理?
    • 为何要选择glide?
    • 如何防止内存泄漏?
    • OOM机制?

    glide流程分析

    先来一张glide整体流程图


    整体流程.png glide时序图.png

    glide请求过程中thumbRequest作用

    缩略图请求

    glide是如何进行缓存的?

    • 加载图片

      glide在加载图片时 会先从弱引用列表查找 再从LRU缓存中查找 再从网络加载
      弱引用列表是以空间换时间的一种方式 更加的高效

    • 缓存图片

      • 内部缓存存储的入口,等同于调用Engine.onEngineJobComplete()
        EngineJob.notifyCallbacksOfResult()
        -> engineJobListener.onEngineJobComplete(this, localKey, localResource);
      • 如果开启内存缓存的话,将解析后的图片添加到弱引用缓存,添加前先封装成 ResourceWeakReference对象,如果key有重复的则将之前的弱引用列表的中对应的数据制空。
        Engine.onEngineJobComplete()
        -> activeResources.activate(key, resource);
      • 磁盘缓存的入口
        DecodeJob.notifyEncodeAndRelease()
        ->deferredEncodeManager.encode(diskCacheProvider, options);
    查找缓存.png 加入缓存.png

    glide如何进行内存管理?

    1. 弱引用+LRU算法
    2. 监听TrimMemory和LowMemory

    如何防止内存泄漏?

    初始化glide的时候 根据传入的context生成一个一个无UI的fragment 将fragment的lifecycle传入requestManager中

    OOM机制?

    1. 引入largeHeap属性,让系统为App分配更多的独立内存。
    2. 禁止Glide内存缓存。设置skipMemoryCache(true)。
    3. 自定义GlideModule。设置MemoryCache和BitmapPool大小。
    4. 升级到Glide4.0,使用asDrawable代替asBitmap,drawable更省内存。
    5. ImageView的scaleType为fitXY时,改为fitCenter/centerCrop/fitStart/fitEnd显示。
    6. 不使用application作为context。当context为application时,会把imageView是生命周期延长到整个运行过程中,imageView不能被回收,从而造成OOM异常。
    7. 使用application作为context。但是对ImageView使用弱引用或软引用,尽量使用SoftReference,当内存不足时,将及时回收无用的ImageView。
    8. 当列表在滑动的时候,调用Glide的pauseRequests()取消请求,滑动停止时,调用resumeRequests()恢复请求。
    9. Try catch某些大内存分配的操作。考虑在catch里面尝试一次降级的内存分配操作。例如decode bitmap的时候,catch到OOM,可以尝试把采样比例再增加一倍之后,再次尝试decode。
    10. BitmapFactory.Options和BitmapFactory.decodeStream获取原始图片的宽、高,绕过Java层加载Bitmap,再调用Glide的override(width,height)控制显示。
    11. 图片局部加载。参考:SubsamplingScaleImageView,先将图片下载到本地,然后去加载,只加载当前可视区域,在手指拖动的时候再去加载另外的区域。

    为何要选择glide?

    • 链式调用。
    • 默认使用RGB_565格式(1像素占2byte),Picasso使用RGB_8888格式(1像素4byte)。
    • 入侵性低,使用标准的ImageView,无需使用自定义的View。
    • 支持的格式多(png,jpg,gif,webp,video等...)。
    • 缓存处理好(正在使用的通过弱引用缓存,已使用过的通过Lru缓存,并且可缓存不同格式)。
    • 可感知页面生命周期,动态控制图片加载。防止出现内存泄露。
    • 性能好,能防止频繁主线程I/O导致的页面闪烁卡顿。

    glide 源码解析

    with()方法

    看一下代码

    
     @NonNull
      public static RequestManager with(@NonNull Context context) {
        /*
        * 上面的注释的意思就是说 如果你的资源用在这个fragment上  就用这个fragment的context  别瞎用别的
        * 总结  初始化glide  创建透明fragment  将生命周期绑定到RequestManager上
        * */
        return getRetriever(context).get(context);
      }
      
      
       @NonNull
      private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        // Context could be null for other reasons (ie the user passes in null), but in practice it will
        // only occur due to errors with the Fragment lifecycle.
        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();
      }
      
      /**
       * Get the singleton.
       *
       * @return the singleton
       */
      @NonNull
      public static Glide get(@NonNull Context context) {
        if (glide == null) {
          GeneratedAppGlideModule annotationGeneratedModule =
              getAnnotationGeneratedGlideModules(context.getApplicationContext());
          synchronized (Glide.class) {
            if (glide == null) {
              checkAndInitializeGlide(context, annotationGeneratedModule);
            }
          }
        }
    
        return glide;
      }
      
      //初始化glide
       @GuardedBy("Glide.class")
      @SuppressWarnings("deprecation")
      private static void initializeGlide(
          @NonNull Context context,
          @NonNull GlideBuilder builder,
          @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
                    
                     .......
    
        
        //初始化glide
        Glide glide = builder.build(applicationContext);
            Glide.glide = glide;
            
                     .......
      }
      
       @NonNull
      public RequestManager get(@NonNull Context context) {
        if (context == null) {
          throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (Util.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
              // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
              // Context#createPackageContext may return a Context without an Application instance,
              // in which case a ContextWrapper may be used to attach one.
              && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
            return get(((ContextWrapper) context).getBaseContext());
          }
        }
    
        return getApplicationManager(context);
      }
    
    
     //创建一个透明的fragment 并绑定到requestmanager上 避免内存泄漏
     @NonNull
      public RequestManager get(@NonNull FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
          return get(activity.getApplicationContext());
        } else {
          assertNotDestroyed(activity);
          FragmentManager fm = activity.getSupportFragmentManager();
          return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
        }
      }
      
       @NonNull
      private RequestManager supportFragmentGet(
          @NonNull Context context,
          @NonNull FragmentManager fm,
          @Nullable Fragment parentHint,
          boolean isParentVisible) {
        SupportRequestManagerFragment current =
            getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
          // TODO(b/27524013): factor out this glide.get() call.
          Glide glide = Glide.get(context);
          //新建一个透明的fragment  绑定fragment的lifecycle到requestManager上
          requestManager =
              factory.build(
                  glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
          current.setRequestManager(requestManager);
        }
        return requestManager;
      }
      
    

    load()方法

      /**
       * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Bitmap)}.
       *
       * @return A new request builder for loading a {@link Drawable} using the given model.
       */
      @NonNull
      @CheckResult
      @Override
      public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
        /*
        * asDrawable() 构建RequestBuilder对象
        * load() 覆盖一下配置
        * */
        return asDrawable().load(bitmap);
      }
    
    

    into() 重点就在into方法上

     @NonNull
      public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
        /*
        * 注释的意思  就是取消正在加载的请求  释放已经加载到ImageView的资源 加载需要的资源到ImageView中
        * */
        Util.assertMainThread();
        Preconditions.checkNotNull(view);
    
        //设置scaletype
        BaseRequestOptions<?> requestOptions = this;
        if (!requestOptions.isTransformationSet()
            && requestOptions.isTransformationAllowed()
            && view.getScaleType() != null) {
          // Clone in this method so that if we use this RequestBuilder to load into a View and then
          // into a different target, we don't retain the transformation applied based on the previous
          // View's scale type.
          switch (view.getScaleType()) {
            case CENTER_CROP:
              requestOptions = requestOptions.clone().optionalCenterCrop();
              break;
            case CENTER_INSIDE:
              requestOptions = requestOptions.clone().optionalCenterInside();
              break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
              requestOptions = requestOptions.clone().optionalFitCenter();
              break;
            case FIT_XY:
              requestOptions = requestOptions.clone().optionalCenterInside();
              break;
            case CENTER:
            case MATRIX:
            default:
              // Do nothing.
          }
        }
    
        return into(
            glideContext.buildImageViewTarget(view, transcodeClass),
            /*targetListener=*/ null,
            requestOptions,
            Executors.mainThreadExecutor());
      }
    

    然后我们跟着看into()方法 into方法主要是做一些检查 下面三个方法也很重要 我们一会会回来看这个方法 先留着

      private <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> options,
          Executor callbackExecutor) {
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
        
        //构建request
        Request request = buildRequest(target, targetListener, options, callbackExecutor);
    
        //如果request和前面一样  或者正在运行 就继续
        Request previous = target.getRequest();
        if (request.isEquivalentTo(previous)
            && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
          // If the request is completed, beginning again will ensure the result is re-delivered,
          // triggering RequestListeners and Targets. If the request is failed, beginning again will
          // restart the request, giving it another chance to complete. If the request is already
          // running, we can let it continue running without interruption.
          if (!Preconditions.checkNotNull(previous).isRunning()) {
            // Use the previous request rather than the new one to allow for optimizations like skipping
            // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
            // that are done in the individual Request.
            previous.begin();
          }
          return target;
        }
    
        //下面三个方法很重要  我们一会再回来看
        requestManager.clear(target);
        target.setRequest(request);
        requestManager.track(target, request);
    
        return target;
      }
    

    我们看一下buildRequest方法 长长的一大段啊 不过我们看下去发现最后构造了SingleRequest对象

      private Request buildRequest(
          Target<TranscodeType> target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> requestOptions,
          Executor callbackExecutor) {
        return buildRequestRecursive(
            /*requestLock=*/ new Object(),
            target,
            targetListener,
            /*parentCoordinator=*/ null,
            transitionOptions,
            requestOptions.getPriority(),
            requestOptions.getOverrideWidth(),
            requestOptions.getOverrideHeight(),
            requestOptions,
            callbackExecutor);
      }
    
      private Request buildRequestRecursive(
          Object requestLock,
          Target<TranscodeType> target,
          @Nullable RequestListener<TranscodeType> targetListener,
          @Nullable RequestCoordinator parentCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          BaseRequestOptions<?> requestOptions,
          Executor callbackExecutor) {
    
        // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
        //先新建错误请求协调
        ErrorRequestCoordinator errorRequestCoordinator = null;
        if (errorBuilder != null) {
          errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
          parentCoordinator = errorRequestCoordinator;
        }
    
        //创建Request
        Request mainRequest =
            buildThumbnailRequestRecursive(
                requestLock,
                target,
                targetListener,
                parentCoordinator,
                transitionOptions,
                priority,
                overrideWidth,
                overrideHeight,
                requestOptions,
                callbackExecutor);
    
        if (errorRequestCoordinator == null) {
          return mainRequest;
        }
    
        int errorOverrideWidth = errorBuilder.getOverrideWidth();
        int errorOverrideHeight = errorBuilder.getOverrideHeight();
        if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {
          errorOverrideWidth = requestOptions.getOverrideWidth();
          errorOverrideHeight = requestOptions.getOverrideHeight();
        }
    
        Request errorRequest =
            errorBuilder.buildRequestRecursive(
                requestLock,
                target,
                targetListener,
                errorRequestCoordinator,
                errorBuilder.transitionOptions,
                errorBuilder.getPriority(),
                errorOverrideWidth,
                errorOverrideHeight,
                errorBuilder,
                callbackExecutor);
        errorRequestCoordinator.setRequests(mainRequest, errorRequest);
        return errorRequestCoordinator;
      }
    
      private Request buildThumbnailRequestRecursive(
          Object requestLock,
          Target<TranscodeType> target,
          RequestListener<TranscodeType> targetListener,
          @Nullable RequestCoordinator parentCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          BaseRequestOptions<?> requestOptions,
          Executor callbackExecutor) {
        if (thumbnailBuilder != null) {
          // Recursive case: contains a potentially recursive thumbnail request builder.
          if (isThumbnailBuilt) {
            throw new IllegalStateException(
                "You cannot use a request as both the main request and a "
                    + "thumbnail, consider using clone() on the request(s) passed to thumbnail()");
          }
    
          TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
              thumbnailBuilder.transitionOptions;
    
          // Apply our transition by default to thumbnail requests but avoid overriding custom options
          // that may have been applied on the thumbnail request explicitly.
          if (thumbnailBuilder.isDefaultTransitionOptionsSet) {
            thumbTransitionOptions = transitionOptions;
          }
    
          Priority thumbPriority =
              thumbnailBuilder.isPrioritySet()
                  ? thumbnailBuilder.getPriority()
                  : getThumbnailPriority(priority);
    
          int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();
          int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();
          if (Util.isValidDimensions(overrideWidth, overrideHeight)
              && !thumbnailBuilder.isValidOverride()) {
            thumbOverrideWidth = requestOptions.getOverrideWidth();
            thumbOverrideHeight = requestOptions.getOverrideHeight();
          }
    
          ThumbnailRequestCoordinator coordinator =
              new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
          Request fullRequest =
              obtainRequest(
                  requestLock,
                  target,
                  targetListener,
                  requestOptions,
                  coordinator,
                  transitionOptions,
                  priority,
                  overrideWidth,
                  overrideHeight,
                  callbackExecutor);
          isThumbnailBuilt = true;
          // Recursively generate thumbnail requests.
          Request thumbRequest =
              thumbnailBuilder.buildRequestRecursive(
                  requestLock,
                  target,
                  targetListener,
                  coordinator,
                  thumbTransitionOptions,
                  thumbPriority,
                  thumbOverrideWidth,
                  thumbOverrideHeight,
                  thumbnailBuilder,
                  callbackExecutor);
          isThumbnailBuilt = false;
          coordinator.setRequests(fullRequest, thumbRequest);
          return coordinator;
        } else if (thumbSizeMultiplier != null) {
          // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
          ThumbnailRequestCoordinator coordinator =
              new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
          Request fullRequest =
              obtainRequest(
                  requestLock,
                  target,
                  targetListener,
                  requestOptions,
                  coordinator,
                  transitionOptions,
                  priority,
                  overrideWidth,
                  overrideHeight,
                  callbackExecutor);
          BaseRequestOptions<?> thumbnailOptions =
              requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);
    
          Request thumbnailRequest =
              obtainRequest(
                  requestLock,
                  target,
                  targetListener,
                  thumbnailOptions,
                  coordinator,
                  transitionOptions,
                  getThumbnailPriority(priority),
                  overrideWidth,
                  overrideHeight,
                  callbackExecutor);
    
          coordinator.setRequests(fullRequest, thumbnailRequest);
          return coordinator;
        } else {
          // Base case: no thumbnail.
          return obtainRequest(
              requestLock,
              target,
              targetListener,
              requestOptions,
              parentCoordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight,
              callbackExecutor);
        }
      }
    
      private Request obtainRequest(
          Object requestLock,
          Target<TranscodeType> target,
          RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> requestOptions,
          RequestCoordinator requestCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          Executor callbackExecutor) {
        return SingleRequest.obtain(
            context,
            glideContext,
            requestLock,
            model,
            transcodeClass,
            requestOptions,
            overrideWidth,
            overrideHeight,
            priority,
            target,
            targetListener,
            requestListeners,
            requestCoordinator,
            glideContext.getEngine(),
            transitionOptions.getTransitionFactory(),
            callbackExecutor);
      }
    
    

    我们已经构造了request对象 接着看上面的三行方法 这边省的往上翻 直接又拷贝了一份过来

     //下面三个方法很重要  我们一会再回来看
        requestManager.clear(target);
        target.setRequest(request);
        requestManager.track(target, request);
    

    我们看track方法

     synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
        targetTracker.track(target);
        requestTracker.runRequest(request);
      }
      
        /** Starts tracking the given request. */
      public void runRequest(@NonNull Request request) {
        requests.add(request);
        if (!isPaused) {
          request.begin();
        } else {
          request.clear();
          if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Paused, delaying request");
          }
          pendingRequests.add(request);
        }
      }
    
    

    我们刚上面分析了 request对象其实是SingleRequest

      @Override
      public void begin() {
        synchronized (requestLock) {
          assertNotCallingCallbacks();
          //确定对象没有被回收
          stateVerifier.throwIfRecycled();
          startTime = LogTime.getLogTime();
          if (model == null) {
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
              width = overrideWidth;
              height = overrideHeight;
            }
            // Only log at more verbose log levels if the user has set a fallback drawable, because
            // fallback Drawables indicate the user expects null models occasionally.
            int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
            onLoadFailed(new GlideException("Received null model"), logLevel);
            return;
          }
    
          if (status == Status.RUNNING) {
            throw new IllegalArgumentException("Cannot restart a running request");
          }
    
          // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
          // that starts an identical request into the same Target or View), we can simply use the
          // resource and size we retrieved the last time around and skip obtaining a new size, starting
          // a new load etc. This does mean that users who want to restart a load because they expect
          // that the view size has changed will need to explicitly clear the View or Target before
          // starting the new load.
          if (status == Status.COMPLETE) {
            onResourceReady(resource, DataSource.MEMORY_CACHE);
            return;
          }
    
          // Restarts for requests that are neither complete nor running can be treated as new requests
          // and can run again from the beginning.
    
          //初始化状态
          status = Status.WAITING_FOR_SIZE;
          //如果已经设置好width
          if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            onSizeReady(overrideWidth, overrideHeight);
          } else {
            //异步  还是会调用onSizeReady
            target.getSize(this);
          }
    
          if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
              && canNotifyStatusChanged()) {
            target.onLoadStarted(getPlaceholderDrawable());
          }
          if (IS_VERBOSE_LOGGABLE) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
          }
        }
      }
    

    核心方法就在onSizeReady()中了

      /** A callback method that should never be invoked directly. */
      @Override
      public void onSizeReady(int width, int height) {
        
        ........
        
        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);
                  
            .......
        }
        
      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) {
        long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    
        //构建key 我们可以确定 存储在内存中的key 和width height 原图有关
        EngineKey key =
            keyFactory.buildKey(
                model,
                signature,
                width,
                height,
                transformations,
                resourceClass,
                transcodeClass,
                options);
    
        EngineResource<?> memoryResource;
        synchronized (this) {
          //1.先从内存中获取
          memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
    
          if (memoryResource == null) {
            return waitForExistingOrStartNewJob(
                glideContext,
                model,
                signature,
                width,
                height,
                resourceClass,
                transcodeClass,
                priority,
                diskCacheStrategy,
                transformations,
                isTransformationRequired,
                isScaleOnlyOrNoTransform,
                options,
                isMemoryCacheable,
                useUnlimitedSourceExecutorPool,
                useAnimationPool,
                onlyRetrieveFromCache,
                cb,
                callbackExecutor,
                key,
                startTime);
          }
        }
    
        // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
        // deadlock.
        cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
        return null;
      }
      
    
    

    从缓存中尝试获取

      @Nullable
      private EngineResource<?> loadFromMemory(
          EngineKey key, boolean isMemoryCacheable, long startTime) {
        //1.1是否调过缓存 对应skipMemoryCacheOf
        if (!isMemoryCacheable) {
          return null;
        }
        //1.2
        EngineResource<?> active = loadFromActiveResources(key);
        if (active != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
          }
          return active;
        }
        //1.3
        EngineResource<?> cached = loadFromCache(key);
        if (cached != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
          }
          return cached;
        }
    
        return null;
      }
    
      private static void logWithTimeAndKey(String log, long startTime, Key key) {
        Log.v(TAG, log + " in " + LogTime.getElapsedMillis(startTime) + "ms, key: " + key);
      }
    
      @Nullable
      private EngineResource<?> loadFromActiveResources(Key key) {
        //1.2.1 从弱引用缓存列表中取  如果能获取到 则从弱引用列表中获取 并删除key
        EngineResource<?> active = activeResources.get(key);
        if (active != null) {
          active.acquire();
        }
    
        return active;
      }
    
      private EngineResource<?> loadFromCache(Key key) {
        //1.3.1
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
          cached.acquire();
          activeResources.activate(key, cached);
        }
        return cached;
      }
    
      private EngineResource<?> getEngineResourceFromCache(Key key) {
        //1.3.2 获取cache
        Resource<?> cached = cache.remove(key);
    
        final EngineResource<?> result;
        if (cached == null) {
          result = null;
        } else if (cached instanceof EngineResource) {
          // Save an object allocation if we've cached an EngineResource (the typical case).
          result = (EngineResource<?>) cached;
        } else {
          result =
              new EngineResource<>(
                  cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
        }
        return result;
      }
    

    没获取到的话就调用waitForExistingOrStartNewJob()方法

      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);
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Added to existing load", startTime, key);
          }
          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);
        //decodeJob 是个runnable 调用start方法其实就是调用decodeJob的run方法
        engineJob.start(decodeJob);
    
        if (VERBOSE_IS_LOGGABLE) {
          logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
      }
    

    查看DecodeJob的run方法

      // We need to rethrow only CallbackException, but not other types of Throwables.
      @SuppressWarnings("PMD.AvoidRethrowingException")
      @Override
      public void run() {
       
        .......
        
          //核心方法
          runWrapped();
        } catch (CallbackException e) {
          // If a callback not controlled by Glide throws an exception, we should avoid the Glide
          // specific debug logic below.
          throw e;
        } catch (Throwable t) {
          // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
          // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
          // are however ensuring that our callbacks are always notified when a load fails. Without this
          // notification, uncaught throwables never notify the corresponding callbacks, which can cause
          // loads to silently hang forever, a case that's especially bad for users using Futures on
          // background threads.
          if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(
                TAG,
                "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
                t);
          }
          // When we're encoding we've already notified our callback and it isn't safe to do so again.
          if (stage != Stage.ENCODE) {
            throwables.add(t);
            notifyFailed();
          }
          if (!isCancelled) {
            throw t;
          }
          throw t;
        } finally {
          // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
          // close in all cases anyway.
          if (localFetcher != null) {
            localFetcher.cleanup();
          }
          GlideTrace.endSection();
        }
      }
    
    

    查看runwrapped方法

      private void runWrapped() {
        switch (runReason) {
          case INITIALIZE:
    //        1.1
            stage = getNextStage(Stage.INITIALIZE);
            currentGenerator = getNextGenerator();
            runGenerators();
            break;
          case SWITCH_TO_SOURCE_SERVICE:
            runGenerators();
            break;
          case DECODE_DATA:
            decodeFromRetrievedData();
            break;
          default:
            throw new IllegalStateException("Unrecognized run reason: " + runReason);
        }
      }
    
      private DataFetcherGenerator getNextGenerator() {
    //    1.2
        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);
          case FINISHED:
            return null;
          default:
            throw new IllegalStateException("Unrecognized stage: " + stage);
        }
      }
    
      private void runGenerators() {
    //    1.3
        currentThread = Thread.currentThread();
        startFetchTime = LogTime.getLogTime();
        boolean isStarted = false;
        while (!isCancelled
            && currentGenerator != null
            && !(isStarted = currentGenerator.startNext())) {
          stage = getNextStage(stage);
          currentGenerator = getNextGenerator();
    
          if (stage == Stage.SOURCE) {
            reschedule();
            return;
          }
        }
        // We've run out of stages and generators, give up.
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
          notifyFailed();
        }
    
        // Otherwise a generator started a new load and we expect to be called back in
        // onDataFetcherReady.
      }
    

    这里会调用currentGenerator.startNext()方法
    先后调用ResourceCacheGenerator, DataCacheGenerator
    最终会调用SourceGenerator.startNext()方法

      @Override
      public boolean startNext() {
        //缓存数据
        if (dataToCache != null) {
          Object data = dataToCache;
          dataToCache = null;
          cacheData(data);
        }
    
        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
          return true;
        }
        sourceCacheGenerator = null;
    
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
          //获取LoadData
          loadData = helper.getLoadData().get(loadDataListIndex++);
          if (loadData != null
              && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
                  || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
            started = true;
            //开始加载
            startNextLoad(loadData);
          }
        }
        return started;
      }
    
      private void startNextLoad(final LoadData<?> toStart) {
        //这里的fetcher 其实是HttpUrlFetch
        loadData.fetcher.loadData(
            helper.getPriority(),
            new DataCallback<Object>() {
              @Override
              public void onDataReady(@Nullable Object data) {
                if (isCurrentRequest(toStart)) {
                  //数据回调
                  onDataReadyInternal(toStart, data);
                }
              }
    
              @Override
              public void onLoadFailed(@NonNull Exception e) {
                if (isCurrentRequest(toStart)) {
                  onLoadFailedInternal(toStart, e);
                }
              }
            });
      }
    
    

    接下来就是通过HTTP请求获取数据

      @Override
      public void loadData(
          @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
        long startTime = LogTime.getLogTime();
        try {
          //通过http获取数据
          InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
          callback.onDataReady(result);
        } catch (IOException e) {
          if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Failed to load data for url", e);
          }
          callback.onLoadFailed(e);
        } finally {
          if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
          }
        }
      }
    
      private InputStream loadDataWithRedirects(
          URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
          throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
          // Comparing the URLs using .equals performs additional network I/O and is generally broken.
          // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
          try {
            if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
              throw new HttpException("In re-direct loop");
            }
          } catch (URISyntaxException e) {
            // Do nothing, this is best effort.
          }
        }
    
        //获取HttpurlConnection
        urlConnection = connectionFactory.build(url);
        for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
          urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(timeout);
        urlConnection.setReadTimeout(timeout);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);
    
        // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
        // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
        urlConnection.setInstanceFollowRedirects(false);
    
        // Connect explicitly to avoid errors in decoders if connection fails.
        urlConnection.connect();
        // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
        stream = urlConnection.getInputStream();
        if (isCancelled) {
          return null;
        }
        final int statusCode = urlConnection.getResponseCode();
        //判断响应码
        if (isHttpOk(statusCode)) {
          //把响应流返回
          return getStreamForSuccessfulRequest(urlConnection);
        } else if (isHttpRedirect(statusCode)) {
          String redirectUrlString = urlConnection.getHeaderField("Location");
          if (TextUtils.isEmpty(redirectUrlString)) {
            throw new HttpException("Received empty or null redirect url");
          }
          URL redirectUrl = new URL(url, redirectUrlString);
          // Closing the stream specifically is required to avoid leaking ResponseBodys in addition
          // to disconnecting the url connection below. See #2352.
          cleanup();
          return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else if (statusCode == INVALID_STATUS_CODE) {
          throw new HttpException(statusCode);
        } else {
          throw new HttpException(urlConnection.getResponseMessage(), statusCode);
        }
      }
    

    接下来数据其实已经获取到了 我们接下来要做的就是缓存 解码 显示

    编码的代码真的是又臭又长 我不想复制了 直接看源码吧 我把最后一步复制上来

      private static Bitmap decodeStream(
          ImageReader imageReader,
          BitmapFactory.Options options,
          DecodeCallbacks callbacks,
          BitmapPool bitmapPool)
          throws IOException {
        if (!options.inJustDecodeBounds) {
          // Once we've read the image header, we no longer need to allow the buffer to expand in
          // size. To avoid unnecessary allocations reading image data, we fix the mark limit so that it
          // is no larger than our current buffer size here. We need to do so immediately before
          // decoding the full image to avoid having our mark limit overridden by other calls to
          // mark and reset. See issue #225.
          callbacks.onObtainBounds();
          imageReader.stopGrowingBuffers();
        }
    
        // BitmapFactory.Options out* variables are reset by most calls to decodeStream, successful or
        // otherwise, so capture here in case we log below.
        int sourceWidth = options.outWidth;
        int sourceHeight = options.outHeight;
        String outMimeType = options.outMimeType;
        final Bitmap result;
        TransformationUtils.getBitmapDrawableLock().lock();
        try {
          result = imageReader.decodeBitmap(options);
        } catch (IllegalArgumentException e) {
          IOException bitmapAssertionException =
              newIoExceptionForInBitmapAssertion(e, sourceWidth, sourceHeight, outMimeType, options);
          if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(
                TAG,
                "Failed to decode with inBitmap, trying again without Bitmap re-use",
                bitmapAssertionException);
          }
          if (options.inBitmap != null) {
            try {
              //加入bitmap池
              bitmapPool.put(options.inBitmap);
              options.inBitmap = null;
              return decodeStream(imageReader, options, callbacks, bitmapPool);
            } catch (IOException resetException) {
              throw bitmapAssertionException;
            }
          }
          throw bitmapAssertionException;
        } finally {
          TransformationUtils.getBitmapDrawableLock().unlock();
        }
    
        return result;
      }
    

    这时候我们已经拿到了bitmap对象 接下来还要做数据转换

    //BitmapDrawableTranscoder.transcode()
    public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
        return LazyBitmapDrawableResource.obtain(resources, toTranscode);
      }
    
    //BitmapDrawableTranscoder.transcode()
    @Nullable
    public static Resource<BitmapDrawable> obtain(Resources resources, Resource<Bitmap> bitmapResource) {
        if (bitmapResource == null) {
          return null;
        }
        return new LazyBitmapDrawableResource(resources, bitmapResource);
    }
    

    参考

    小飞机

    相关文章

      网友评论

        本文标题:Glide源码解析

        本文链接:https://www.haomeiwen.com/subject/hsmkjktx.html