Glide之EngineJob和EngineResource

EngineJob中的代码不多,整体思路也比较简单。

A class that manages a load by adding and removing callbacks for for the load and notifying callbacks when the load completes.

EngineJob管理加载,维护着callback的添加和移除,当加载完成时通知维护的所有callback。

上面是官方对EngineJob的注释,基本描述清楚了EngineJob的用途,需要补充的是,EngineJob还负责在请求过程中的线程切换,比如,一开始从主线程切换到DiskCacheExecutor线程,尝试从磁盘获取数据,如果获取不到,线程切换到SourceExecutor中的线程去网络获取,获取到又切换了一次线程,只不过用的还是SourceExecutor中的线程去把数据存到磁盘,取出数据,然后回调给EngineJob,最后切换到主线程,这样主线程中就可以拿到数据了。

不过在详细分析EngineJob过程中,有涉及到EngineResource的缓存和释放逻辑,我们分析一下:

 void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        // TODO: Seems like we might as well put this in the memory cache instead of just recycling
        // it since we've gotten this far...
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
      } else if (hasResource) {
        throw new IllegalStateException("Already have resource");
      }
      engineResource = engineResourceFactory.build(resource, isCacheable);
      // Hold on to resource for duration of our callbacks below so we don't recycle it in the
      // middle of notifying if it synchronously released by one of the callbacks. Acquire it under
      // a lock here so that any newly added callback that executes before the next locked section
      // below can't recycle the resource before we call the callbacks.
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }

    listener.onEngineJobComplete(this, localKey, localResource);

    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }
Hold on to resource for duration of our callbacks below so we don't recycle it in the middle of notifying if it synchronously released by one of the callbacks. Acquire it under a lock here so that any newly added callback that executes before the next locked section below can't recycle the resource before we call the callbacks.

上面这段英文尝试翻译一下

在下面所有回调期间保持resource不释放,所以,假如其中一个回调同步释放,我们也要保证不回收resouce。我们先获取resource,使得在我们调用完所有callback之前,任何callback都不能释放resource。

 

我们先说明一下resouce是怎么释放的,这里的resouce是EngineResource,看看EngineResouce的release方法

  void release() {
    // To avoid deadlock, always acquire the listener lock before our lock so that the locking
    // scheme is consistent (Engine -> EngineResource). Violating this order leads to deadlock
    // (b/123646037).
    synchronized (listener) {
      synchronized (this) {
        if (acquired <= 0) {
          throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (--acquired == 0) {
          listener.onResourceReleased(key, this);
        }
      }
    }
  }

acquired变量表示有多少地方引用了这Resource,如果acquired等于0的话,就把EngineResource真正释放,onResourceReleased在Engine里面,我们在缓存文章中会分析。

我们在看看EngineJob中callback执行的代码,entry.executor.execute(new CallResourceReady(entry.cb));这块代码就是到主线程里面执行callback,我们看看CallResourceRready这个类


  private class CallResourceReady implements Runnable {

    private final ResourceCallback cb;

    CallResourceReady(ResourceCallback cb) {
      this.cb = cb;
    }

    @Override
    public void run() {
      synchronized (EngineJob.this) {
        if (cbs.contains(cb)) {
          // Acquire for this particular callback.
          engineResource.acquire();
          callCallbackOnResourceReady(cb);
          removeCallback(cb);
        }
        decrementPendingCallbacks();
      }
    }
  }

我们看engineResource.acquire(); 这句话,就是把acquired的变量加1,表示有一个地方引用了EngineResource。callCallbackOnResourceReady里面调用了回调,我们考虑一种情况,假如在callback中调用EngineReource的release。这样的话,就可能出问题,这样一次执行,acquired变成了0,导致EngineReouse被释放了,这是不对的,因为还可能有其它callback。

所以,想了一招,在执行callback之前,先把有多少个callback记下,pendingCallbacks,同时调用engineResource.acquire();先把acquired加1,这样在callback中调用EngineReource的release也不怕了,因为acquired不会变成0了,每执行完一个callback把pendingCallbacks数量减1,直到pendingCallbacks等于0了,调用一次EngineReource的release,让acquired变成真实的值。

第二部分

我们再看看notifyCallbacksOfResult中的 listener.onEngineJobComplete(this, localKey, localResource);这句话,listener是Engine

  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
      resource.setResourceListener(key, this);

      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }

    jobs.removeIfCurrent(key, engineJob);
  }

就是把EngineResource放到ActiveResources缓存里面。

第三部分

我们再来看看EngineResource被释放的时候,具体做了什么,看EngineResource release中的listener.onResourceReleased(key, this);这句话,listener是Engine

  @Override
  public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

就是把EngineResource中ActiveResources缓存中移除,放到LruResourceCache缓存里面。

第四部分

我们上面还差一部分没说,就是EngineResource的acquire是怎么变成0的?

这块内容就涉及到Glide的生命周期了,我们知道Glide的请求过程是能够感知到宿主的生命周期的。

我们看看RequestManager的onDestroy方法:

  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }

我们知道一个FragmentManager有唯一的一个RequestManager,调用RequestManager的load方法会new一个RequestBuilder,看这个名字就知道它的使命是实例化Request,事实上也是调用RequestBuilder的into方法就会实例化一个SingleRequest,一个Request就代表一次请求。

需要说明的是,每次请求和目标对象都是由RequestManager维护的,RequestManager中有RequestTracker维护和这个RM相关的所有Request,有TargetTracker维护和这个RM相关的所有Target。

说到这里我们再看RequestManager的onDestroy方法,requestTracker.clearRequests()就是在页面退出的时候,把所有的Request清除掉的

  /**
   * Cancels all requests and clears their resources.
   *
   * <p>After this call requests cannot be restarted.
   */
  public void clearRequests() {
    for (Request request : Util.getSnapshot(requests)) {
      // It's unsafe to recycle the Request here because we don't know who might else have a
      // reference to it.
      clearRemoveAndMaybeRecycle(request, /*isSafeToRecycle=*/ false);
    }
    pendingRequests.clear();
  }


  private boolean clearRemoveAndMaybeRecycle(@Nullable Request request, boolean isSafeToRecycle) {
     if (request == null) {
       // If the Request is null, the request is already cleared and we don't need to search further
       // for its owner.
      return true;
    }
    boolean isOwnedByUs = requests.remove(request);
    // Avoid short circuiting.
    isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
    if (isOwnedByUs) {
      request.clear();
      if (isSafeToRecycle) {
        request.recycle();
      }
    }
    return isOwnedByUs;
  }

看request.clear()调用

  /**
   * Cancels the current load if it is in progress, clears any resources held onto by the request
   * and replaces the loaded resource if the load completed with the placeholder.
   *
   * <p>Cleared requests can be restarted with a subsequent call to {@link #begin()}
   *
   * @see #cancel()
   */
  @Override
  public synchronized void clear() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    if (status == Status.CLEARED) {
      return;
    }
    cancel();
    // Resource must be released before canNotifyStatusChanged is called.
    if (resource != null) {
      releaseResource(resource);
    }
    if (canNotifyCleared()) {
      target.onLoadCleared(getPlaceholderDrawable());
    }

    status = Status.CLEARED;
  }

  private void releaseResource(Resource<?> resource) {
    engine.release(resource);
    this.resource = null;
  }

继续看engine.release(resource)

  public void release(Resource<?> resource) {
    if (resource instanceof EngineResource) {
      ((EngineResource<?>) resource).release();
    } else {
      throw new IllegalArgumentException("Cannot release anything but an EngineResource");
    }
  }

看到没?调用了EngineResource的release方法,上面分析过了。

 

总结:

通过上面的分析,我们知道:

(1)数据Resource被请求回来包装成EngineResource后,是先放到ActiveResource内存缓存里面的。

(2)只要EngineResource有被持有,也就是acquire不等于0,它就一直缓存在ActiveResources中。

(3)EngineResource没有被持有了,也就是acquire等于0了,它就被缓存到LruResourceCache,或者被释放,根据设置条件来的。

 

学习内容:

(1)Request在RequestTracker是怎么维护的

(2)Request是怎么被回收掉的

是在RequestManager的onDestroy中处理的,调用了Request的recycle方法,把Request回收到了对象池里面。

(3)EngineJob是怎么被回收的

EngineJob用到了对象池,具体分析看Glide(Android)之对象池。当EngineJob中注册的所有callback被回调完成就会回收EngineJob,EngineJob的回调分析看Glide之DecodeJob decode、transcode过程和EngineJob回调的处理。我们看看EngineJob回收的地方

  @Synthetic
  synchronized void decrementPendingCallbacks() {
    stateVerifier.throwIfRecycled();
    Preconditions.checkArgument(isDone(), "Not yet complete!");
    int decremented = pendingCallbacks.decrementAndGet();
    Preconditions.checkArgument(decremented >= 0, "Can't decrement below 0");
    if (decremented == 0) {
      if (engineResource != null) {
        engineResource.release();
      }

      release();
    }
  }

  private synchronized void release() {
    if (key == null) {
      throw new IllegalArgumentException();
    }
    cbs.clear();
    key = null;
    engineResource = null;
    resource = null;
    hasLoadFailed = false;
    isCancelled = false;
    hasResource = false;
    decodeJob.release(/*isRemovedFromQueue=*/ false);
    decodeJob = null;
    exception = null;
    dataSource = null;
    pool.release(this);
  }

看到没?在回收EngineJob之前先回收了DecodeJob,DecodeJob也使用了对象池。

(4)一次网络请求回来,哪些对象还活着?哪些对象被回收了?哪些对象被释放了?

EngineJob被回收到了对象池Pools.Pool里面

DecodeJob也被回收到了对象池Pools.Pool里面(是在EngineJob被回收前),DecodeJob中持有的对象都被释放了

SingleRequest是活着的,它被RequestManager中的RequestTracker维护着,SingleRequest中持有请求回来的数据。

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章