ㄷㅣㅆㅣ's Amusement

[Android/Java] Glide, source analysis. -- 2. Flow 본문

Programming/Android

[Android/Java] Glide, source analysis. -- 2. Flow

ㄷㅣㅆㅣ 2016. 12. 9. 15:34

[Android/Java] Glide, source analysis. -- 2. Flow

Glide

You will Save yourself a lot of time & headache.


지난 포스팅에서는....



 - Glide를 사용하는 것이 안드로이드 개발에서는 매우 일반적이 되어버렸고, 그때문에 더이상 이미지에 의한 OOM(Out Of Memory)문제를 겪지 않겠지만, 그래도 알고 쓰는것과 모르고 쓰는 것에는 매우 큰 차이가 있다.

 - 또한 Glide가 아무리 안정적이더라도 신이 인간을 어여삐 여겨 내려준 코드가 아닌 이상 왕도는 없으므로, 프로젝트 성격에 의해 커스터마이징 할 필요가 있을지도 모르니 Flow라도 알아보자.

- 이번 시리즈는 몇편이 마지막일지도 모르겠고, 참고할 자료라고는 오로지 소스코드밖에 없으니...  조바심 내지 않고 차근차근 써내려가보겠다. 

- 시리즈의 2번째인 이번 포스팅은 다소 성의없어보일 수도 있겠으나... Flow를 설명하기에 이보다 명료한 방법이 없을 것 같으니 궁금한 점이 생기면 언제든 댓글 달아주세요. 


  • When the operation started to run.
    1
    2
    3
    4
    Glide
    .with(context)
    .load(/*IMAGE_PATH*/)
    .into(/*IMAGE_VIEW*/);
    cs


  1. with
  2. get
  3. Glide.get —> createGlide
  4. to return created glide
  5. RequestManager 생성
    1. Glide 호출된 액티비티의 fragment RequestManagerFragment 감싸고, lifecycle 리스너를 등록한다.
    2. ConnectivityMonitor 생성
    3. glide객체에 자신을 등록한다. (glide.setRequestManager)
  6. to return requestManager
  7. to return requestManager
  8. to return requestManager
  9. requestManager 어떻게 불러올 것인지 세팅한다.
  10. requestBuilder 생성
  11. to return requestBuilder
  12. to return requestBuilder
  13. target 이미지를 할당한다.
    1. target 기존 작업을 가지고있으면 취소
  14. request 생성
    1. status pending으로 설정
  15. to return request
  16. track()
  17. TargetTracker target(ImageViewTarget) 등록
  18. RequestTracker request 등록
  19. begin()
    1. status WAITING_FOR_SIZE 변경
    2. 기존에 width, height 계산했었다면(?) onSizeReady호출, 아니라면 계산 호출 
    3. onSizeReady()에서는 status RUNNING으로 변경하고, engine.load() 호출.
    4. ImageViewTarget PlaceHolder 세팅
  20. load() (이 부분부터는 3부에서 다룹니다)
    1. EngineJob 만든다
    2. DecodeJob 만든다
    3. EngineJob으로 Decode한다. (? --> [Android/Java] 병렬 프로그래밍 : Executor Framework에대한 고찰 참고.)
      1. ModelLoader에서 decode
      2. 복잡한 decode step 수행하고 Request onResourceReady() 호출
      3. ImageViewTarget setResource() 호출하여 이미제 세팅


---------------- 이하 Call Sequence에 의한 source code ---------------------
  • Get suitable "Request Manager" (1~8)

    1
    2
    3
    4
    5
    6
    7
    // Glide.java
    // ".with()"

    public static RequestManager with(Context context) {
      RequestManagerRetriever retriever = RequestManagerRetriever.get();
      return retriever.get(context);
    }
    cs
      • Using "Request Manager Retriever"
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        // RequestManagerRetriever.java
        public RequestManager get(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) {
              return get(((ContextWrapper) context).getBaseContext());
            }
          }
         
          return getApplicationManager(context);
        }
         
        public RequestManager get(FragmentActivity activity) {
          if (Util.isOnBackgroundThread()) {
            return get(activity.getApplicationContext());
          } else {
            assertNotDestroyed(activity);
            FragmentManager fm = activity.getSupportFragmentManager();
            return supportFragmentGet(activity, fm, null);
          }
        }
         
        public RequestManager get(Fragment fragment) {
          if (fragment.getActivity() == null) {
            throw new IllegalArgumentException(
                "You cannot start a load on a fragment before it is attached");
          }
          if (Util.isOnBackgroundThread()) {
            return get(fragment.getActivity().getApplicationContext());
          } else {
            FragmentManager fm = fragment.getChildFragmentManager();
            return supportFragmentGet(fragment.getActivity(), fm, fragment);
          }
        }
        cs
  • Making RequestBuilder and add the transition options then object loading  (9~12)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    // RequestManager.java
    // ".load"
     
    /**
     * Returns a request builder to load the given {@link java.lang.String}. signature.
     *
     * <p> Note - this method caches data using only the given String as the cache key. If the data is
     * a Uri outside of your control, or you otherwise expect the data represented by the given String
     * to change without the String identifier changing, Consider using
     * {@link com.bumptech.glide.request.BaseRequestOptions#signature(com.bumptech.glide.load.Key)} to
     * mixin a signature you create that identifies the data currently at the given String that will
     * invalidate the cache if that data changes. Alternatively, using
     * {@link com.bumptech.glide.load.engine.DiskCacheStrategy#NONE} and/or
     * {@link com.bumptech.glide.request.BaseRequestOptions#skipMemoryCache(boolean)} may be
     * appropriate.
     * </p>
     *
     * @see #load(Object)
     *
     * @param string A file path, or a uri or url handled by
     * {@link com.bumptech.glide.load.model.UriLoader}.
     */
    public RequestBuilder<TranscodeType> load(@Nullable String string) {
      return loadGeneric(string);
    }
     
    /**
     * Attempts to always load the resource using any registered {@link
     * com.bumptech.glide.load.ResourceDecoder}s that can decode any subclass of {@link Drawable}.
     *
     * <p> By default, may return either a {@link android.graphics.drawable.BitmapDrawable} or {@link
     * GifDrawable}, but if additional decoders are registered for other {@link Drawable} subclasses,
     * any of those subclasses may also be returned. </p>
     *
     * @return A new request builder for loading a {@link Drawable}.
     */
    public RequestBuilder<Drawable> asDrawable() {
      return as(Drawable.class).transition(new DrawableTransitionOptions());
    }
     
    /**
     * A helper method equivalent to calling {@link #asDrawable()} and then {@link
     * RequestBuilder#load(Object)} with the given model.
     *
     * @return A new request builder for loading a {@link Drawable} using the given model.
     */
    public RequestBuilder<Drawable> load(@Nullable Object model) {
      return asDrawable().load(model);
    }
     
     
    cs
    • To Request to load
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      // RequestBuilder.java
      // ".load"

      /**
       * Set the target the resource will be loaded into.
       *
       * @param target The target to load the resource into.
       * @return The given target.
       * @see RequestManager#clear(Target)
       */
      public <extends Target<TranscodeType>> Y into(@NonNull Y target) {
        Util.assertMainThread();
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
       
        Request previous = target.getRequest();
       
         if (previous != null) {
          requestManager.clear(target);
        }
       
        requestOptions.lock();
        Request request = buildRequest(target);
        target.setRequest(request);
        requestManager.track(target, request);
       
        return target;
      }
       
      /**
       * Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
       * the view, and frees any resources Glide may have previously loaded into the view so they may be
       * reused.
       *
       * @see RequestManager#clear(Target)
       *
       * @param view The view to cancel previous loads for and load the new resource into.
       * @return The
       * {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
       */
      public Target<TranscodeType> into(ImageView view) {
        Util.assertMainThread();
        Preconditions.checkNotNull(view);
       
        if (!requestOptions.isTransformationSet()
            && requestOptions.isTransformationAllowed()
            && view.getScaleType() != null) {
          if (requestOptions.isLocked()) {
            requestOptions = requestOptions.clone();
          }
          switch (view.getScaleType()) {
            case CENTER_CROP:
              requestOptions.optionalCenterCrop(context);
              break;
            case CENTER_INSIDE:
              requestOptions.optionalCenterInside(context);
              break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
              requestOptions.optionalFitCenter(context);
              break;
            //$CASES-OMITTED$
              default:
              // Do nothing.
          }
        }
       
        return into(context.buildImageViewTarget(view, transcodeClass));
      }
      cs


  • Build Request (14~15)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    // RequestBuilder.java

    private Request obtainRequest(Target<TranscodeType> target,
        BaseRequestOptions<?> requestOptions, RequestCoordinator requestCoordinator,
        TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority,
        int overrideWidth, int overrideHeight) {
      requestOptions.lock();
     
      return SingleRequest.obtain(
          context,
          model,
          transcodeClass,
          requestOptions,
          overrideWidth,
          overrideHeight,
          priority,
          target,
          requestListener,
          requestCoordinator,
          context.getEngine(),
          transitionOptions.getTransitionFactory());
    }
     
    private Request buildRequest(Target<TranscodeType> target) {
        return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(),
                requestOptions.getOverrideWidth(), 
                requestOptions.getOverrideHeight());
      }
     
    private Request buildRequestRecursive(Target<TranscodeType> target,
          @Nullable ThumbnailRequestCoordinator parentCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority, int overrideWidth, int overrideHeight) {
        // Base case: no thumbnail.
          return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority,
              overrideWidth, overrideHeight);
    }
     
    /**
       * Set the target the resource will be loaded into.
       *
       * @param target The target to load the resource into.
       * @return The given target.
       * @see RequestManager#clear(Target)
       */
      public <extends Target<TranscodeType>> Y into(@NonNull Y target) {
        Util.assertMainThread();
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
     
        Request previous = target.getRequest();
     
        if (previous != null) {
          requestManager.clear(target);
        }
     
        requestOptions.lock();
        Request request = buildRequest(target);
        target.setRequest(request);
        requestManager.track(target, request);
     
        return target;
      }
    cs
  • Add track (17~18)
    1
    2
    3
    4
    5
    // RequestManager.java
    void track(Target<?> target, Request request) {
        targetTracker.track(target);
        requestTracker.runRequest(request);
    }
    cs
    • begin request (19)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      /**
      * Starts tracking the given request.
      */
      public void runRequest(Request request) {
          requests.add(request);
          if (!isPaused) {
              request.begin();
          } else {
              pendingRequests.add(request);
          }
      }
      cs
  • load(20)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // SingleReqeust.java
     
    @Override
    public void begin() {
      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;
      }
     
      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }
     
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
     
    cs







%% 모든 소스코드는 이곳에서 다운로드 할 수 있습니다 %%

Glide : https://github.com/bumptech/glide

0 Comments
댓글쓰기 폼