All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bumptech.glide.load.engine.executor.GlideExecutor Maven / Gradle / Ivy

Go to download

A fast and efficient image loading library for Android focused on smooth scrolling.

There is a newer version: 5.0.0-rc01
Show newest version
package com.bumptech.glide.load.engine.executor;

import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.bumptech.glide.util.Synthetic;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * A prioritized {@link ThreadPoolExecutor} for running jobs in Glide.
 */
public final class GlideExecutor implements ExecutorService {
  /**
   * The default thread name prefix for executors used to load/decode/transform data not found in
   * cache.
   */
  private static final String DEFAULT_SOURCE_EXECUTOR_NAME = "source";

  /**
   * The default thread name prefix for executors used to load/decode/transform data found in
   * Glide's cache.
   */
  private static final String DEFAULT_DISK_CACHE_EXECUTOR_NAME = "disk-cache";

  /**
   * The default thread count for executors used to load/decode/transform data found in Glide's
   * cache.
   */
  private static final int DEFAULT_DISK_CACHE_EXECUTOR_THREADS = 1;

  private static final String TAG = "GlideExecutor";

  /**
   * The default thread name prefix for executors from unlimited thread pool used to
   * load/decode/transform data not found in cache.
   */
  private static final String SOURCE_UNLIMITED_EXECUTOR_NAME = "source-unlimited";

  private static final String ANIMATION_EXECUTOR_NAME = "animation";

  /**
   * The default keep alive time for threads in our cached thread pools in milliseconds.
   */
  private static final long KEEP_ALIVE_TIME_MS = TimeUnit.SECONDS.toMillis(10);

  // Don't use more than four threads when automatically determining thread count..
  private static final int MAXIMUM_AUTOMATIC_THREAD_COUNT = 4;

  // May be accessed on other threads, but this is an optimization only so it's ok if we set its
  // value more than once.
  private static volatile int bestThreadCount;

  private final ExecutorService delegate;

  /**
   * Returns a new fixed thread pool with the default thread count returned from
   * {@link #calculateBestThreadCount()}, the {@link #DEFAULT_DISK_CACHE_EXECUTOR_NAME} thread name
   * prefix, and the
   * {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy#DEFAULT}
   * uncaught throwable strategy.
   *
   * 

Disk cache executors do not allow network operations on their threads. */ public static GlideExecutor newDiskCacheExecutor() { return newDiskCacheExecutor( DEFAULT_DISK_CACHE_EXECUTOR_THREADS, DEFAULT_DISK_CACHE_EXECUTOR_NAME, UncaughtThrowableStrategy.DEFAULT); } /** * Returns a new fixed thread pool with the default thread count returned from * {@link #calculateBestThreadCount()}, the {@link #DEFAULT_DISK_CACHE_EXECUTOR_NAME} thread name * prefix, and a custom * {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} * uncaught throwable strategy. * *

Disk cache executors do not allow network operations on their threads. * @param uncaughtThrowableStrategy The {@link * com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} to use to * handle uncaught exceptions. */ // Public API. @SuppressWarnings("unused") public static GlideExecutor newDiskCacheExecutor( UncaughtThrowableStrategy uncaughtThrowableStrategy) { return newDiskCacheExecutor( DEFAULT_DISK_CACHE_EXECUTOR_THREADS, DEFAULT_DISK_CACHE_EXECUTOR_NAME, uncaughtThrowableStrategy); } /** * Returns a new fixed thread pool with the given thread count, thread name prefix, * and {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy}. * *

Disk cache executors do not allow network operations on their threads. * * @param threadCount The number of threads. * @param name The prefix for each thread name. * @param uncaughtThrowableStrategy The {@link * com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} to use to * handle uncaught exceptions. */ // Public API. @SuppressWarnings("WeakerAccess") public static GlideExecutor newDiskCacheExecutor( int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) { return new GlideExecutor( new ThreadPoolExecutor( threadCount /* corePoolSize */, threadCount /* maximumPoolSize */, 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new PriorityBlockingQueue(), new DefaultThreadFactory(name, uncaughtThrowableStrategy, true))); } /** * Returns a new fixed thread pool with the default thread count returned from * {@link #calculateBestThreadCount()}, the {@link #DEFAULT_SOURCE_EXECUTOR_NAME} thread name * prefix, and the * {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy#DEFAULT} * uncaught throwable strategy. * *

Source executors allow network operations on their threads. */ public static GlideExecutor newSourceExecutor() { return newSourceExecutor( calculateBestThreadCount(), DEFAULT_SOURCE_EXECUTOR_NAME, UncaughtThrowableStrategy.DEFAULT); } /** * Returns a new fixed thread pool with the default thread count returned from * {@link #calculateBestThreadCount()}, the {@link #DEFAULT_SOURCE_EXECUTOR_NAME} thread name * prefix, and a custom * {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} * uncaught throwable strategy. * *

Source executors allow network operations on their threads. * * @param uncaughtThrowableStrategy The {@link * com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} to use to * handle uncaught exceptions. */ // Public API. @SuppressWarnings("unused") public static GlideExecutor newSourceExecutor( UncaughtThrowableStrategy uncaughtThrowableStrategy) { return newSourceExecutor( calculateBestThreadCount(), DEFAULT_SOURCE_EXECUTOR_NAME, uncaughtThrowableStrategy); } /** * Returns a new fixed thread pool with the given thread count, thread name prefix, * and {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy}. * *

Source executors allow network operations on their threads. * * @param threadCount The number of threads. * @param name The prefix for each thread name. * @param uncaughtThrowableStrategy The {@link * com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy} to use to * handle uncaught exceptions. */ // Public API. @SuppressWarnings("WeakerAccess") public static GlideExecutor newSourceExecutor( int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) { return new GlideExecutor( new ThreadPoolExecutor( threadCount /* corePoolSize */, threadCount /* maximumPoolSize */, 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new PriorityBlockingQueue(), new DefaultThreadFactory(name, uncaughtThrowableStrategy, false))); } /** * Returns a new unlimited thread pool with zero core thread count to make sure no threads are * created by default, {@link #KEEP_ALIVE_TIME_MS} keep alive * time, the {@link #SOURCE_UNLIMITED_EXECUTOR_NAME} thread name prefix, the * {@link com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy#DEFAULT} * uncaught throwable strategy, and the {@link SynchronousQueue} since using default unbounded * blocking queue, for example, {@link PriorityBlockingQueue} effectively won't create more than * {@code corePoolSize} threads. * See * ThreadPoolExecutor documentation. * *

Source executors allow network operations on their threads. */ public static GlideExecutor newUnlimitedSourceExecutor() { return new GlideExecutor(new ThreadPoolExecutor( 0, Integer.MAX_VALUE, KEEP_ALIVE_TIME_MS, TimeUnit.MILLISECONDS, new SynchronousQueue(), new DefaultThreadFactory( SOURCE_UNLIMITED_EXECUTOR_NAME, UncaughtThrowableStrategy.DEFAULT, false))); } /** * Returns a new cached thread pool that defaults to either one or two threads depending on the * number of available cores to use when loading frames of animations. */ public static GlideExecutor newAnimationExecutor() { int bestThreadCount = calculateBestThreadCount(); // We don't want to add a ton of threads running animations in parallel with our source and // disk cache executors. Doing so adds unnecessary CPU load and can also dramatically increase // our maximum memory usage. Typically one thread is sufficient here, but for higher end devices // with more cores, two threads can provide better performance if lots of GIFs are showing at // once. int maximumPoolSize = bestThreadCount >= 4 ? 2 : 1; return newAnimationExecutor(maximumPoolSize, UncaughtThrowableStrategy.DEFAULT); } /** * Returns a new cached thread pool with the given thread count and * {@link UncaughtThrowableStrategy} to use when loading frames of animations. */ // Public API. @SuppressWarnings("WeakerAccess") public static GlideExecutor newAnimationExecutor( int threadCount, UncaughtThrowableStrategy uncaughtThrowableStrategy) { return new GlideExecutor( new ThreadPoolExecutor( 0 /* corePoolSize */, threadCount, KEEP_ALIVE_TIME_MS, TimeUnit.MILLISECONDS, new PriorityBlockingQueue(), new DefaultThreadFactory( ANIMATION_EXECUTOR_NAME, uncaughtThrowableStrategy, true))); } @VisibleForTesting GlideExecutor(ExecutorService delegate) { this.delegate = delegate; } @Override public void execute(@NonNull Runnable command) { delegate.execute(command); } @NonNull @Override public Future submit(@NonNull Runnable task) { return delegate.submit(task); } @NonNull @Override public List> invokeAll(@NonNull Collection> tasks) throws InterruptedException { return delegate.invokeAll(tasks); } @NonNull @Override public List> invokeAll( @NonNull Collection> tasks, long timeout, @NonNull TimeUnit unit) throws InterruptedException { return delegate.invokeAll(tasks, timeout, unit); } @NonNull @Override public T invokeAny(@NonNull Collection> tasks) throws InterruptedException, ExecutionException { return delegate.invokeAny(tasks); } @Override public T invokeAny( @NonNull Collection> tasks, long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate.invokeAny(tasks, timeout, unit); } @NonNull @Override public Future submit(@NonNull Runnable task, T result) { return delegate.submit(task, result); } @Override public Future submit(@NonNull Callable task) { return delegate.submit(task); } @Override public void shutdown() { delegate.shutdown(); } @NonNull @Override public List shutdownNow() { return delegate.shutdownNow(); } @Override public boolean isShutdown() { return delegate.isShutdown(); } @Override public boolean isTerminated() { return delegate.isTerminated(); } @Override public boolean awaitTermination(long timeout, @NonNull TimeUnit unit) throws InterruptedException { return delegate.awaitTermination(timeout, unit); } @Override public String toString() { return delegate.toString(); } /** * Determines the number of cores available on the device. */ // Public API. @SuppressWarnings("WeakerAccess") public static int calculateBestThreadCount() { if (bestThreadCount == 0) { bestThreadCount = Math.min(MAXIMUM_AUTOMATIC_THREAD_COUNT, RuntimeCompat.availableProcessors()); } return bestThreadCount; } /** * A strategy for handling unexpected and uncaught {@link Throwable}s thrown by futures run on the * pool. */ public interface UncaughtThrowableStrategy { /** * Silently catches and ignores the uncaught {@link Throwable}s. */ // Public API. @SuppressWarnings("unused") UncaughtThrowableStrategy IGNORE = new UncaughtThrowableStrategy() { @Override public void handle(Throwable t) { //ignore } }; /** * Logs the uncaught {@link Throwable}s using {@link #TAG} and {@link Log}. */ UncaughtThrowableStrategy LOG = new UncaughtThrowableStrategy() { @Override public void handle(Throwable t) { if (t != null && Log.isLoggable(TAG, Log.ERROR)) { Log.e(TAG, "Request threw uncaught throwable", t); } } }; /** * Rethrows the uncaught {@link Throwable}s to crash the app. */ // Public API. @SuppressWarnings("unused") UncaughtThrowableStrategy THROW = new UncaughtThrowableStrategy() { @Override public void handle(Throwable t) { if (t != null) { throw new RuntimeException("Request threw uncaught throwable", t); } } }; /** The default strategy, currently {@link #LOG}. */ UncaughtThrowableStrategy DEFAULT = LOG; void handle(Throwable t); } /** * A {@link java.util.concurrent.ThreadFactory} that builds threads slightly above priority {@link * android.os.Process#THREAD_PRIORITY_BACKGROUND}. */ private static final class DefaultThreadFactory implements ThreadFactory { private static final int DEFAULT_PRIORITY = android.os.Process.THREAD_PRIORITY_BACKGROUND + android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE; private final String name; @Synthetic final UncaughtThrowableStrategy uncaughtThrowableStrategy; @Synthetic final boolean preventNetworkOperations; private int threadNum; DefaultThreadFactory(String name, UncaughtThrowableStrategy uncaughtThrowableStrategy, boolean preventNetworkOperations) { this.name = name; this.uncaughtThrowableStrategy = uncaughtThrowableStrategy; this.preventNetworkOperations = preventNetworkOperations; } @Override public synchronized Thread newThread(@NonNull Runnable runnable) { final Thread result = new Thread(runnable, "glide-" + name + "-thread-" + threadNum) { @Override public void run() { android.os.Process.setThreadPriority(DEFAULT_PRIORITY); if (preventNetworkOperations) { StrictMode.setThreadPolicy( new ThreadPolicy.Builder() .detectNetwork() .penaltyDeath() .build()); } try { super.run(); } catch (Throwable t) { uncaughtThrowableStrategy.handle(t); } } }; threadNum++; return result; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy