public RequestManager get(Fragment fragment) { Preconditions.checkNotNull(fragment.getActivity(), "You cannot start a load on a fragment before it is attached or after it is destroyed"); if (Util.isOnBackgroundThread()) { return get(fragment.getActivity().getApplicationContext()); } else { FragmentManager fm = fragment.getChildFragmentManager(); return supportFragmentGet(fragment.getActivity(), fm, fragment); } }
/** * Cancel any pending loads Glide may have for the target and free any resources (such as * {@link Bitmap}s) that may have been loaded for the target so they may be reused. * * @param target The Target to cancel loads for. */ public void clear(@Nullable final Target<?> target) { if (target == null) { return; } if (Util.isOnMainThread()) { untrackOrDelegate(target); } else { mainHandler.post(new Runnable() { @Override public void run() { clear(target); } }); } }
private void release(boolean isRemovedFromQueue) { Util.assertMainThread(); cbs.clear(); key = null; engineResource = null; resource = null; if (ignoredCallbacks != null) { ignoredCallbacks.clear(); } hasLoadFailed = false; isCancelled = false; hasResource = false; decodeJob.release(isRemovedFromQueue); decodeJob = null; exception = null; dataSource = null; pool.release(this); }
@SuppressWarnings({"PMD.SimplifyBooleanReturns", "RedundantIfStatement"}) @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MediaStoreSignature that = (MediaStoreSignature) o; if (dateModified != that.dateModified) { return false; } if (orientation != that.orientation) { return false; } if (!Util.bothNullOrEqual(mimeType, that.mimeType)) { return false; } return true; }
@Override @Nullable public Bitmap get(int width, int height, Bitmap.Config config) { final int size = Util.getBitmapByteSize(width, height, config); Key key = keyPool.get(size); Integer possibleSize = sortedSizes.ceilingKey(size); if (possibleSize != null && possibleSize != size && possibleSize <= size * MAX_SIZE_MULTIPLE) { keyPool.offer(key); key = keyPool.get(possibleSize); } // Do a get even if we know we don't have a bitmap so that the key moves to the front in the // lru pool final Bitmap result = groupedMap.get(key); if (result != null) { result.reconfigure(width, height, config); decrementBitmapOfSize(possibleSize); } return result; }
/** * 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(); break; case CENTER_INSIDE: requestOptions.optionalCenterInside(); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions.optionalFitCenter(); break; case FIT_XY: requestOptions.optionalCenterInside(); break; case CENTER: case MATRIX: default: // Do nothing. } } return into(context.buildImageViewTarget(view, transcodeClass)); }
/** * Returns a future that can be used to do a blocking get on a background thread. * * @param width The desired width in pixels, or {@link Target#SIZE_ORIGINAL}. This will be * overridden by * {@link com.bumptech.glide.request.RequestOptions#override(int, int)} if * previously called. * @param height The desired height in pixels, or {@link Target#SIZE_ORIGINAL}. This will be * overridden by * {@link com.bumptech.glide.request.RequestOptions#override(int, int)}} if * previously called). */ public FutureTarget<TranscodeType> submit(int width, int height) { final RequestFutureTarget<TranscodeType> target = new RequestFutureTarget<>(context.getMainHandler(), width, height); if (Util.isOnBackgroundThread()) { context.getMainHandler().post(new Runnable() { @Override public void run() { if (!target.isCancelled()) { into(target); } } }); } else { into(target); } return target; }
@Test public void testAddsBitmapsToMemoryCacheIfMemoryCacheHasEnoughSpaceRemaining() { Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); when(cache.getMaxSize()).thenReturn(Util.getBitmapByteSize(bitmap)); PreFillType size = new PreFillType.Builder(bitmap.getWidth(), bitmap.getHeight()).setConfig(bitmap.getConfig()) .build(); Map<PreFillType, Integer> allocationOrder = new HashMap<>(); allocationOrder.put(size, 1); getHandler(allocationOrder).run(); verify(cache).put(any(Key.class), anyResource()); verify(pool, never()).put(any(Bitmap.class)); // TODO(b/20335397): This code was relying on Bitmap equality which Robolectric removed // assertThat(addedBitmaps).containsExactly(bitmap); }
@Test public void testAddsBitmapsToPoolIfMemoryCacheIsNotFullButCannotFitBitmap() { Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); when(cache.getMaxSize()).thenReturn(Util.getBitmapByteSize(bitmap) / 2); PreFillType size = new PreFillType.Builder(bitmap.getWidth(), bitmap.getHeight()).setConfig(bitmap.getConfig()) .build(); Map<PreFillType, Integer> allocationOrder = new HashMap<>(); allocationOrder.put(size, 1); getHandler(allocationOrder).run(); verify(cache, never()).put(any(Key.class), anyResource()); // TODO(b/20335397): This code was relying on Bitmap equality which Robolectric removed //verify(pool).put(eq(bitmap)); //assertThat(addedBitmaps).containsExactly(bitmap); }
@Test public void testAllocationOrderDoesNotOverFillWithMultipleSizes() { PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] { new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT) .setConfig(defaultBitmapConfig).build(), new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT) .setConfig(defaultBitmapConfig).build(), new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2) .setConfig(defaultBitmapConfig).build() }); int byteSize = 0; while (!allocationOrder.isEmpty()) { PreFillType current = allocationOrder.remove(); byteSize += Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig()); } assertThat(byteSize).isIn(Range.atMost(poolSize + cacheSize)); }
@Test public void testAllocationOrderDoesNotOverFillWithMultipleSizesAndWeights() { PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] { new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT) .setConfig(defaultBitmapConfig).setWeight(4).build(), new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT) .setConfig(defaultBitmapConfig).build(), new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 3) .setConfig(defaultBitmapConfig).setWeight(3).build() }); int byteSize = 0; while (!allocationOrder.isEmpty()) { PreFillType current = allocationOrder.remove(); byteSize += Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig()); } assertThat(byteSize).isIn(Range.atMost(poolSize + cacheSize)); }
/** * 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()} </p> * * @see #cancel() */ @Override public void clear() { Util.assertMainThread(); 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()); } // Must be after cancel(). status = Status.CLEARED; }
@Override public boolean isEquivalentTo(Request o) { if (o instanceof SingleRequest) { SingleRequest<?> that = (SingleRequest<?>) o; return overrideWidth == that.overrideWidth && overrideHeight == that.overrideHeight && Util.bothModelsNullEquivalentOrEquals(model, that.model) && transcodeClass.equals(that.transcodeClass) && requestOptions.equals(that.requestOptions) && priority == that.priority // We do not want to require that RequestListeners implement equals/hashcode, so we don't // compare them using equals(). We can however, at least assert that the request listener // is either present or not present in both requests. && (requestListener != null ? that.requestListener != null : that.requestListener == null); } return false; }
@Test public void testAddsBitmapsToPoolIfMemoryCacheIsNotFullButCannotFitBitmap() { Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); when(cache.getMaxSize()).thenReturn((long) Util.getBitmapByteSize(bitmap) / 2); PreFillType size = new PreFillType.Builder(bitmap.getWidth(), bitmap.getHeight()).setConfig(bitmap.getConfig()) .build(); Map<PreFillType, Integer> allocationOrder = new HashMap<>(); allocationOrder.put(size, 1); getHandler(allocationOrder).run(); verify(cache, never()).put(any(Key.class), anyResource()); // TODO(b/20335397): This code was relying on Bitmap equality which Robolectric removed //verify(pool).put(eq(bitmap)); //assertThat(addedBitmaps).containsExactly(bitmap); }
private synchronized R doGet(Long timeoutMillis) throws ExecutionException, InterruptedException, TimeoutException { if (assertBackgroundThread && !isDone()) { Util.assertBackgroundThread(); } if (isCancelled) { throw new CancellationException(); } else if (loadFailed) { throw new ExecutionException(new IllegalStateException("Load failed")); } else if (resultReceived) { return resource; } if (timeoutMillis == null) { waiter.waitForTimeout(this, 0); } else if (timeoutMillis > 0) { waiter.waitForTimeout(this, timeoutMillis); } if (Thread.interrupted()) { throw new InterruptedException(); } else if (loadFailed) { throw new ExecutionException(new IllegalStateException("Load failed")); } else if (isCancelled) { throw new CancellationException(); } else if (!resultReceived) { throw new TimeoutException(); } return resource; }
/** * Immediately calls the given callback with the sizes given in the constructor. * * @param cb {@inheritDoc} */ @Override public final void getSize(SizeReadyCallback cb) { if (!Util.isValidDimensions(width, height)) { throw new IllegalArgumentException( "Width and height must both be > 0 or Target#SIZE_ORIGINAL, but given" + " width: " + width + " and height: " + height + ", either provide dimensions in the constructor" + " or call override()"); } cb.onSizeReady(width, height); }
@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)); } }
@Override @Nullable public Bitmap get(int width, int height, Bitmap.Config config) { int size = Util.getBitmapByteSize(width, height, config); Key bestKey = findBestKey(size, config); Bitmap result = groupedMap.get(bestKey); if (result != null) { // Decrement must be called before reconfigure. decrementBitmapOfSize(bestKey.size, result); result.reconfigure(width, height, result.getConfig() != null ? result.getConfig() : Bitmap.Config.ARGB_8888); } return result; }
/** * Clears as much memory as possible. * * @see android.content.ComponentCallbacks#onLowMemory() * @see android.content.ComponentCallbacks2#onLowMemory() */ public void clearMemory() { // Engine asserts this anyway when removing resources, fail faster and consistently Util.assertMainThread(); // memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687. memoryCache.clearMemory(); bitmapPool.clearMemory(); arrayPool.clearMemory(); }
/** * Starts any not yet completed or failed requests. */ public void resumeRequests() { isPaused = false; for (Request request : Util.getSnapshot(requests)) { if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) { request.begin(); } } pendingRequests.clear(); }
/** * Cancels all requests and clears their resources. * * <p>After this call requests cannot be restarted. */ public void clearRequests() { for (Request request : Util.getSnapshot(requests)) { clearRemoveAndRecycle(request); } pendingRequests.clear(); }
/** * Restarts failed requests and cancels and restarts in progress requests. */ public void restartRequests() { for (Request request : Util.getSnapshot(requests)) { if (!request.isComplete() && !request.isCancelled()) { // Ensure the request will be restarted in onResume. request.pause(); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } } } }
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 /*parentHint*/); } }
@Override public boolean equals(Object o) { if (o instanceof Key) { Key other = (Key) o; return size == other.size && Util.bothNullOrEqual(config, other.config); } return false; }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public RequestManager get(android.app.Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException( "You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return get(fragment.getActivity().getApplicationContext()); } else { android.app.FragmentManager fm = fragment.getChildFragmentManager(); return fragmentGet(fragment.getActivity(), fm, fragment); } }
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod") RequestManager( Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) { this.glide = glide; this.lifecycle = lifecycle; this.treeNode = treeNode; this.requestTracker = requestTracker; final Context context = glide.getGlideContext().getBaseContext(); connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker)); // If we're the application level request manager, we may be created on a background thread. // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread. // This should be entirely safe. if (Util.isOnBackgroundThread()) { mainHandler.post(addSelfToLifecycle); } else { lifecycle.addListener(this); } lifecycle.addListener(connectivityMonitor); setRequestOptions(glide.getGlideContext().getDefaultRequestOptions()); glide.registerRequestManager(this); }
@Override public void onResourceReleased(Key cacheKey, EngineResource<?> resource) { Util.assertMainThread(); activeResources.deactivate(cacheKey); if (resource.isCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource); } }
/** * Performs {@link #resumeRequests()} recursively for all managers that are contextually * descendant to this manager based on the Activity/Fragment hierarchy. The hierarchical semantics * are identical as for {@link #pauseRequestsRecursive()}. */ public void resumeRequestsRecursive() { Util.assertMainThread(); resumeRequests(); for (RequestManager requestManager : treeNode.getDescendants()) { requestManager.resumeRequests(); } }
/** * Performs {@link #resumeRequests()} recursively for all managers that are contextually * descendant to this manager based on the Activity/Fragment hierarchy. The hierarchical semantics * are identical as for {@link #pauseRequestsRecursive()}. */ // Public API. @SuppressWarnings("unused") public void resumeRequestsRecursive() { Util.assertMainThread(); resumeRequests(); for (RequestManager requestManager : treeNode.getDescendants()) { requestManager.resumeRequests(); } }
@Override public void onEngineJobCancelled(EngineJob<?> engineJob, Key key) { Util.assertMainThread(); EngineJob<?> current = jobs.get(key); if (engineJob.equals(current)) { jobs.remove(key); } }
/** * Attempts to allocate {@link android.graphics.Bitmap}s and returns {@code true} if there are * more {@link android.graphics.Bitmap}s to allocate and {@code false} otherwise. */ private boolean allocate() { long start = clock.now(); while (!toPrefill.isEmpty() && !isGcDetected(start)) { PreFillType toAllocate = toPrefill.remove(); final Bitmap bitmap; if (!seenTypes.contains(toAllocate)) { seenTypes.add(toAllocate); bitmap = bitmapPool.getDirty(toAllocate.getWidth(), toAllocate.getHeight(), toAllocate.getConfig()); } else { bitmap = Bitmap.createBitmap(toAllocate.getWidth(), toAllocate.getHeight(), toAllocate.getConfig()); } // Don't over fill the memory cache to avoid evicting useful resources, but make sure it's // not empty so // we use all available space. if (getFreeMemoryCacheBytes() >= Util.getBitmapByteSize(bitmap)) { memoryCache.put(new UniqueKey(), BitmapResource.obtain(bitmap, bitmapPool)); } else { bitmapPool.put(bitmap); } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "allocated [" + toAllocate.getWidth() + "x" + toAllocate.getHeight() + "] " + toAllocate .getConfig() + " size: " + Util.getBitmapByteSize(bitmap)); } } return !isCancelled && !toPrefill.isEmpty(); }
@Override public void put(Bitmap bitmap) { int size = Util.getBitmapByteSize(bitmap); Key key = keyPool.get(size, bitmap.getConfig()); groupedMap.put(key, bitmap); NavigableMap<Integer, Integer> sizes = getSizesForConfig(bitmap.getConfig()); Integer current = sizes.get(key.size); sizes.put(key.size, current == null ? 1 : current + 1); }
@Override public void put(Bitmap bitmap) { int size = Util.getBitmapByteSize(bitmap); final Key key = keyPool.get(size); groupedMap.put(key, bitmap); Integer current = sortedSizes.get(key.size); sortedSizes.put(key.size, current == null ? 1 : current + 1); }
void addCallback(ResourceCallback cb) { Util.assertMainThread(); stateVerifier.throwIfRecycled(); if (hasResource) { cb.onResourceReady(engineResource, dataSource); } else if (hasLoadFailed) { cb.onLoadFailed(exception); } else { cbs.add(cb); } }
@Override @Nullable public Bitmap removeLast() { Bitmap removed = groupedMap.removeLast(); if (removed != null) { final int removedSize = Util.getBitmapByteSize(removed); decrementBitmapOfSize(removedSize); } return removed; }