@Synthetic void handleExceptionOnMainThread() { stateVerifier.throwIfRecycled(); if (isCancelled) { release(false /*isRemovedFromQueue*/); return; } else if (cbs.isEmpty()) { throw new IllegalStateException("Received an exception without any callbacks to notify"); } else if (hasLoadFailed) { throw new IllegalStateException("Already failed once"); } hasLoadFailed = true; listener.onEngineJobComplete(key, null); for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { cb.onLoadFailed(exception); } } release(false /*isRemovedFromQueue*/); }
@Test public void testNotifiesNewCallbackOfResourceIfCallbackIsAddedDuringOnResourceReady() { final EngineJob<Object> job = harness.getJob(); final ResourceCallback existingCallback = mock(ResourceCallback.class); final ResourceCallback newCallback = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.addCallback(newCallback); return null; } }).when(existingCallback).onResourceReady(anyResource(), isADataSource()); job.addCallback(existingCallback); job.start(harness.decodeJob); job.onResourceReady(harness.resource, harness.dataSource); verify(newCallback).onResourceReady(eq(harness.engineResource), eq(harness.dataSource)); }
@Test public void testNotifiesNewCallbackOfExceptionIfCallbackIsAddedDuringOnException() { harness = new EngineJobHarness(); final EngineJob<Object> job = harness.getJob(); final ResourceCallback existingCallback = mock(ResourceCallback.class); final ResourceCallback newCallback = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.addCallback(newCallback); return null; } }).when(existingCallback).onLoadFailed(any(GlideException.class)); GlideException exception = new GlideException("test"); job.addCallback(existingCallback); job.start(harness.decodeJob); job.onLoadFailed(exception); verify(newCallback).onLoadFailed(eq(exception)); }
@Test public void testRemovingCallbackDuringOnResourceReadyIsIgnoredIfCallbackHasAlreadyBeenCalled() { final EngineJob<Object> job = harness.getJob(); final ResourceCallback cb = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.removeCallback(cb); return null; } }).when(cb).onResourceReady(anyResource(), isADataSource()); job.addCallback(cb); job.start(harness.decodeJob); job.onResourceReady(harness.resource, harness.dataSource); verify(cb, times(1)).onResourceReady(anyResource(), isADataSource()); }
@Test public void testRemovingCallbackDuringOnExceptionIsIgnoredIfCallbackHasAlreadyBeenCalled() { harness = new EngineJobHarness(); final EngineJob<Object> job = harness.getJob(); final ResourceCallback cb = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.removeCallback(cb); return null; } }).when(cb).onLoadFailed(any(GlideException.class)); GlideException exception = new GlideException("test"); job.addCallback(cb); job.start(harness.decodeJob); job.onLoadFailed(exception); verify(cb, times(1)).onLoadFailed(eq(exception)); }
@Test public void testRemovingCallbackDuringOnResourceReadyPreventsCallbackFromBeingCalledIfNotYetCalled() { final EngineJob<Object> job = harness.getJob(); final ResourceCallback notYetCalled = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.removeCallback(notYetCalled); return null; } }).when(harness.cb).onResourceReady(anyResource(), isADataSource()); job.addCallback(notYetCalled); job.start(harness.decodeJob); job.onResourceReady(harness.resource, harness.dataSource); verify(notYetCalled, never()).onResourceReady(anyResource(), isADataSource()); }
@Test public void testRemovingCallbackDuringOnResourceReadyPreventsResourceFromBeingAcquiredForCallback() { final EngineJob<Object> job = harness.getJob(); final ResourceCallback notYetCalled = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.removeCallback(notYetCalled); return null; } }).when(harness.cb).onResourceReady(anyResource(), isADataSource()); job.addCallback(notYetCalled); job.start(harness.decodeJob); job.onResourceReady(harness.resource, harness.dataSource); // Once for notifying, once for called. verify(harness.engineResource, times(2)).acquire(); }
@Test public void testRemovingCallbackDuringOnExceptionPreventsCallbackFromBeingCalledIfNotYetCalled() { harness = new EngineJobHarness(); final EngineJob<Object> job = harness.getJob(); final ResourceCallback called = mock(ResourceCallback.class); final ResourceCallback notYetCalled = mock(ResourceCallback.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocationOnMock) throws Throwable { job.removeCallback(notYetCalled); return null; } }).when(called).onLoadFailed(any(GlideException.class)); job.addCallback(called); job.addCallback(notYetCalled); job.start(harness.decodeJob); job.onLoadFailed(new GlideException("test")); verify(notYetCalled, never()).onResourceReady(anyResource(), isADataSource()); }
public MultiCbHarness() { when(factory.build(eq(resource), eq(isCacheable))).thenReturn(engineResource); job = new EngineJob<>( diskCacheService, sourceService, sourceUnlimitedService, animationService, listener, pool, factory).init(key, isCacheable, useUnlimitedSourceGeneratorPool, useAnimationPool); for (int i = 0; i < numCbs; i++) { cbs.add(mock(ResourceCallback.class)); } for (ResourceCallback cb : cbs) { job.addCallback(cb); } }
@Override public void onException(final Exception e) { mainHandler.post(new Runnable() { @Override public void run() { if (isCancelled) { return; } isComplete = true; listener.onEngineJobComplete(key); for (ResourceCallback cb : cbs) { cb.onException(e); } } }); }
public void addCallback(ResourceCallback cb) { Util.assertMainThread(); stateVerifier.throwIfRecycled(); if (hasResource) { cb.onResourceReady(engineResource, dataSource); } else if (hasLoadFailed) { cb.onLoadFailed(exception); } else { cbs.add(cb); } }
public void removeCallback(ResourceCallback cb) { Util.assertMainThread(); stateVerifier.throwIfRecycled(); if (hasResource || hasLoadFailed) { addIgnoredCallback(cb); } else { cbs.remove(cb); if (cbs.isEmpty()) { cancel(); } } }
private void addIgnoredCallback(ResourceCallback cb) { if (ignoredCallbacks == null) { ignoredCallbacks = new ArrayList<>(2); } if (!ignoredCallbacks.contains(cb)) { ignoredCallbacks.add(cb); } }
@Synthetic void handleResultOnMainThread() { stateVerifier.throwIfRecycled(); if (isCancelled) { resource.recycle(); release(false /*isRemovedFromQueue*/); 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); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of // notifying if it synchronously released by one of the callbacks. engineResource.acquire(); listener.onEngineJobComplete(key, engineResource); for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); cb.onResourceReady(engineResource, dataSource); } } // Our request is complete, so we can release the resource. engineResource.release(); release(false /*isRemovedFromQueue*/); }
@Test public void testCallbackIsAddedToExistingRunnerWithExistingLoad() { harness.doLoad(); ResourceCallback newCallback = mock(ResourceCallback.class); harness.cb = newCallback; harness.doLoad(); verify(harness.job).addCallback(eq(newCallback)); }
@Test public void testNotifiesAllCallbacksOnReady() { MultiCbHarness harness = new MultiCbHarness(); harness.job.start(harness.decodeJob); harness.job.onResourceReady(harness.resource, harness.dataSource); for (ResourceCallback cb : harness.cbs) { verify(cb).onResourceReady(eq(harness.engineResource), eq(harness.dataSource)); } }
@Test public void testNotifiesAllCallbacksOnException() { MultiCbHarness harness = new MultiCbHarness(); harness.job.start(harness.decodeJob); GlideException exception = new GlideException("test"); harness.job.onLoadFailed(exception); for (ResourceCallback cb : harness.cbs) { verify(cb).onLoadFailed(eq(exception)); } }
@SuppressWarnings("unchecked") @Test public void removingSomeCallbacksDoesNotCancelRunner() { EngineJob<Object> job = harness.getJob(); job.addCallback(mock(ResourceCallback.class)); job.removeCallback(harness.cb); assertFalse(job.isCancelled()); }
public MultiCbHarness() { when(factory.build(eq(resource), eq(isCacheable))).thenReturn(engineResource); job = new EngineJob<>(diskCacheService, sourceService, sourceUnlimitedService, listener, pool, factory).init(key, isCacheable, useUnlimitedSourceGeneratorPool); for (int i = 0; i < numCbs; i++) { cbs.add(mock(ResourceCallback.class)); } for (ResourceCallback cb : cbs) { job.addCallback(cb); } }
void addCallback(ResourceCallback cb) { Util.assertMainThread(); stateVerifier.throwIfRecycled(); if (hasResource) { cb.onResourceReady(engineResource, dataSource); } else if (hasLoadFailed) { cb.onLoadFailed(exception); } else { cbs.add(cb); } }
@Synthetic void handleResultOnMainThread() { stateVerifier.throwIfRecycled(); if (isCancelled) { resource.recycle(); release(false /*isRemovedFromQueue*/); 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); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of // notifying if it synchronously released by one of the callbacks. engineResource.acquire(); listener.onEngineJobComplete(key, engineResource); int size = cbs.size(); for (int i = 0; i < size; i++) { ResourceCallback cb = cbs.get(i); if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); cb.onResourceReady(engineResource, dataSource); } } // Our request is complete, so we can release the resource. engineResource.release(); release(false /*isRemovedFromQueue*/); }
public SourceResourceRunner(Key key, int width, int height, DataFetcher<T> dataFetcher, ResourceDecoder<T, Z> decoder, Transformation<Z> transformation, ResourceEncoder<Z> encoder, ResourceTranscoder<Z, R> transcoder, DiskCache diskCache, Priority priority, ResourceCallback cb) { this.key = key; this.width = width; this.height = height; this.fetcher = dataFetcher; this.decoder = decoder; this.transformation = transformation; this.encoder = encoder; this.transcoder = transcoder; this.diskCache = diskCache; this.priority = priority; this.cb = cb; }
@Override public void onResourceReady(final Resource resource) { mainHandler.post(new Runnable() { @Override public void run() { if (isCancelled) { resource.recycle(); return; } isComplete = true; // 1 to hold on for duration of request. resource.acquire(cbs.size() + 2); listener.onEngineJobComplete(key); if (isCacheable) { cache.put(key, resource); } else { resource.release(); } for (ResourceCallback cb : cbs) { cb.onResourceReady(resource); } // Our request is complete, so we can release the resource. resource.release(); } }); }