diff --git a/gradle.properties b/gradle.properties index dd0b3a3ae8..6aef2f6b91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ JSR_305_VERSION=3.0.2 AUTO_SERVICE_VERSION=1.0-rc3 JAVAPOET_VERSION=1.9.0 -PMD_VERSION=5.8.1 +PMD_VERSION=6.0.0 FINDBUGS_VERSION=3.0.0 ERROR_PRONE_VERSION=2.1.4-SNAPSHOT ERROR_PRONE_PLUGIN_VERSION=0.0.13 diff --git a/library/build.gradle b/library/build.gradle index 8672d9ddb7..4fa288a7f6 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -22,7 +22,7 @@ dependencies { testImplementation "com.android.support:support-v4:${ANDROID_SUPPORT_VERSION}" if (project.plugins.hasPlugin('net.ltgt.errorprone')) { - errorprone "com.google.errorprone:error_prone_core:${ERROR_PRONE_VERSION}" + errorprone "com.google.errorprone:error_prone_core:${ERROR_PRONE_VERSION}" } } @@ -74,13 +74,9 @@ afterEvaluate { classes = fileTree("${project.buildDir}/intermediates/classes/debug/") source android.sourceSets.main.java.srcDirs - classpath = project.configurations.compile - classpath += files(android.bootClasspath) + classpath = files() doFirst { - it.classpath += - files(project.android.libraryVariants.collect { - it.javaCompile.classpath.files - }) + classpath += classPathForQuality() } effort = 'max' excludeFilter = file("findbugs-exclude.xml") @@ -111,6 +107,15 @@ afterEvaluate { ruleSets = [] ruleSetFiles = files('pmd-ruleset.xml') source android.sourceSets.main.java.srcDirs + classpath = files() + classpath += files("${project.buildDir}/intermediates/classes/debug/") + doFirst { + classpath += classPathForQuality() + } + + //TODO enable this once new Gradle containing this flag is out + //see https://github.com/gradle/gradle/pull/3125#issuecomment-352442432 + //incrementalAnalysis = true // Failures are caught and printed by the violations plugin. ignoreFailures = true @@ -124,4 +129,12 @@ afterEvaluate { check.dependsOn('pmd') } +def classPathForQuality() { + return files( + android.bootClasspath, + project.configurations.compile, + project.android.libraryVariants.collect { it.javaCompile.classpath } + ) +} + apply from: "${rootProject.projectDir}/scripts/upload.gradle" diff --git a/library/pmd-ruleset.xml b/library/pmd-ruleset.xml index da8c961e38..8297a5c09b 100644 --- a/library/pmd-ruleset.xml +++ b/library/pmd-ruleset.xml @@ -5,64 +5,166 @@ Check for flaws in Glide's codebase. - + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - - - - - + - - - - - + + + + + + diff --git a/library/src/main/java/com/bumptech/glide/Glide.java b/library/src/main/java/com/bumptech/glide/Glide.java index 5d2535aa06..1677b1ff20 100644 --- a/library/src/main/java/com/bumptech/glide/Glide.java +++ b/library/src/main/java/com/bumptech/glide/Glide.java @@ -577,6 +577,7 @@ GlideContext getGlideContext() { * {@link com.bumptech.glide.load.engine.prefill.PreFillType.Builder Builders} representing * individual sizes and configurations of {@link android.graphics.Bitmap}s to be pre-filled. */ + @SuppressWarnings("unused") // Public API public void preFillBitmapPool(@NonNull PreFillType.Builder... bitmapAttributeBuilders) { bitmapPreFiller.preFill(bitmapAttributeBuilders); } @@ -644,6 +645,7 @@ public RequestManagerRetriever getRequestManagerRetriever() { * * @return the previous MemoryCategory used by Glide. */ + @SuppressWarnings("WeakerAccess") // Public API @NonNull public MemoryCategory setMemoryCategory(@NonNull MemoryCategory memoryCategory) { // Engine asserts this anyway when removing resources, fail faster and consistently diff --git a/library/src/main/java/com/bumptech/glide/ListPreloader.java b/library/src/main/java/com/bumptech/glide/ListPreloader.java index b30952d12d..a6f5835f8d 100644 --- a/library/src/main/java/com/bumptech/glide/ListPreloader.java +++ b/library/src/main/java/com/bumptech/glide/ListPreloader.java @@ -224,6 +224,8 @@ private void cancelAll() { private static final class PreloadTargetQueue { private final Queue queue; + // The loop is short and the only point is to create the objects. + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") PreloadTargetQueue(int size) { queue = Util.createQueue(size); diff --git a/library/src/main/java/com/bumptech/glide/Registry.java b/library/src/main/java/com/bumptech/glide/Registry.java index fff7cd1355..cce4a86928 100644 --- a/library/src/main/java/com/bumptech/glide/Registry.java +++ b/library/src/main/java/com/bumptech/glide/Registry.java @@ -511,8 +511,11 @@ private List decoderRegistry.getDecoders(dataClass, registeredResourceClass); ResourceTranscoder transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass); - decodePaths.add(new DecodePath<>(dataClass, registeredResourceClass, - registeredTranscodeClass, decoders, transcoder, throwableListPool)); + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + DecodePath path = + new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass, + decoders, transcoder, throwableListPool); + decodePaths.add(path); } } return decodePaths; diff --git a/library/src/main/java/com/bumptech/glide/RequestBuilder.java b/library/src/main/java/com/bumptech/glide/RequestBuilder.java index ec4d145cd5..44ecdfe91d 100644 --- a/library/src/main/java/com/bumptech/glide/RequestBuilder.java +++ b/library/src/main/java/com/bumptech/glide/RequestBuilder.java @@ -546,7 +546,11 @@ public RequestBuilder load(@Nullable byte[] model) { * arguments, the current model is not copied copied so changes to the model will affect both * builders.

*/ - @SuppressWarnings("unchecked") + @SuppressWarnings({ + "unchecked", + // we don't want to throw to be user friendly + "PMD.CloneThrowsCloneNotSupportedException" + }) @CheckResult @Override public RequestBuilder clone() { diff --git a/library/src/main/java/com/bumptech/glide/TransitionOptions.java b/library/src/main/java/com/bumptech/glide/TransitionOptions.java index 99eda46dd0..ce2cfe469b 100644 --- a/library/src/main/java/com/bumptech/glide/TransitionOptions.java +++ b/library/src/main/java/com/bumptech/glide/TransitionOptions.java @@ -71,9 +71,16 @@ public final CHILD transition( return self(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({ + // cast to CHILD is safe given the generic argument represents the object's runtime class + "unchecked", + // CHILD is the correct class name. + "PMD.CloneMethodReturnTypeMustMatchClassName", + // we don't want to throw to be user friendly + "PMD.CloneThrowsCloneNotSupportedException" + }) @Override - protected final CHILD clone() { + public final CHILD clone() { try { return (CHILD) super.clone(); } catch (CloneNotSupportedException e) { diff --git a/library/src/main/java/com/bumptech/glide/load/MultiTransformation.java b/library/src/main/java/com/bumptech/glide/load/MultiTransformation.java index 18e710188d..aaac6cd684 100644 --- a/library/src/main/java/com/bumptech/glide/load/MultiTransformation.java +++ b/library/src/main/java/com/bumptech/glide/load/MultiTransformation.java @@ -18,7 +18,7 @@ public class MultiTransformation implements Transformation { @SafeVarargs @SuppressWarnings("varargs") public MultiTransformation(Transformation... transformations) { - if (transformations.length < 1) { + if (transformations.length == 0) { throw new IllegalArgumentException( "MultiTransformation must contain at least one Transformation"); } diff --git a/library/src/main/java/com/bumptech/glide/load/data/HttpUrlFetcher.java b/library/src/main/java/com/bumptech/glide/load/data/HttpUrlFetcher.java index d150608b48..e8f914b6ea 100644 --- a/library/src/main/java/com/bumptech/glide/load/data/HttpUrlFetcher.java +++ b/library/src/main/java/com/bumptech/glide/load/data/HttpUrlFetcher.java @@ -27,6 +27,10 @@ public class HttpUrlFetcher implements DataFetcher { @VisibleForTesting static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory(); + /** + * Returned when a connection error prevented us from receiving an http error. + */ + private static final int INVALID_STATUS_CODE = -1; private final GlideUrl glideUrl; private final int timeout; @@ -51,23 +55,19 @@ public HttpUrlFetcher(GlideUrl glideUrl, int timeout) { public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { long startTime = LogTime.getLogTime(); - final InputStream result; try { - result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, - glideUrl.getHeaders()); + InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); + callback.onDataReady(result); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); - return; - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime) - + " ms and loaded " + result); + } finally { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); + } } - callback.onDataReady(result); } private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, @@ -108,9 +108,9 @@ private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, return null; } final int statusCode = urlConnection.getResponseCode(); - if (statusCode / 100 == 2) { + if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); - } else if (statusCode / 100 == 3) { + } else if (isHttpRedirect(statusCode)) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url"); @@ -120,13 +120,23 @@ private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, // to disconnecting the url connection below. See #2352. cleanup(); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); - } else if (statusCode == -1) { + } else if (statusCode == INVALID_STATUS_CODE) { throw new HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } } + // Referencing constants is less clear than a simple static method. + private static boolean isHttpOk(int statusCode) { + return statusCode / 100 == 2; + } + + // Referencing constants is less clear than a simple static method. + private static boolean isHttpRedirect(int statusCode) { + return statusCode / 100 == 3; + } + private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws IOException { if (TextUtils.isEmpty(urlConnection.getContentEncoding())) { diff --git a/library/src/main/java/com/bumptech/glide/load/data/mediastore/ThumbnailStreamOpener.java b/library/src/main/java/com/bumptech/glide/load/data/mediastore/ThumbnailStreamOpener.java index 323f02e17a..7749477acd 100644 --- a/library/src/main/java/com/bumptech/glide/load/data/mediastore/ThumbnailStreamOpener.java +++ b/library/src/main/java/com/bumptech/glide/load/data/mediastore/ThumbnailStreamOpener.java @@ -3,6 +3,8 @@ import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import com.bumptech.glide.load.ImageHeaderParser; @@ -24,13 +26,13 @@ class ThumbnailStreamOpener { private final ContentResolver contentResolver; private final List parsers; - public ThumbnailStreamOpener( + ThumbnailStreamOpener( List parsers, ThumbnailQuery query, ArrayPool byteArrayPool, ContentResolver contentResolver) { this(parsers, DEFAULT_SERVICE, query, byteArrayPool, contentResolver); } - public ThumbnailStreamOpener( + ThumbnailStreamOpener( List parsers, FileService service, ThumbnailQuery query, @@ -43,13 +45,13 @@ public ThumbnailStreamOpener( this.parsers = parsers; } - public int getOrientation(Uri uri) { + int getOrientation(Uri uri) { InputStream is = null; try { is = contentResolver.openInputStream(uri); return ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool); - // openInputStream can throw NPEs. - } catch (IOException | NullPointerException e) { + // PMD.AvoidCatchingNPE framework method openInputStream can throw NPEs. + } catch (@SuppressWarnings("PMD.AvoidCatchingNPE") IOException | NullPointerException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to open uri: " + uri, e); } @@ -66,37 +68,43 @@ public int getOrientation(Uri uri) { } public InputStream open(Uri uri) throws FileNotFoundException { - Uri thumbnailUri = null; - InputStream inputStream = null; + String path = getPath(uri); + if (TextUtils.isEmpty(path)) { + return null; + } + + File file = service.get(path); + if (!isValid(file)) { + return null; + } + + Uri thumbnailUri = Uri.fromFile(file); + try { + return contentResolver.openInputStream(thumbnailUri); + // PMD.AvoidCatchingNPE framework method openInputStream can throw NPEs. + } catch (@SuppressWarnings("PMD.AvoidCatchingNPE") NullPointerException e) { + throw (FileNotFoundException) + new FileNotFoundException("NPE opening uri: " + uri + " -> " + thumbnailUri).initCause(e); + } + } + @Nullable + private String getPath(@NonNull Uri uri) { final Cursor cursor = query.query(uri); try { - if (cursor == null || !cursor.moveToFirst()) { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(0); + } else { return null; } - String path = cursor.getString(0); - if (TextUtils.isEmpty(path)) { - return null; - } - - File file = service.get(path); - if (service.exists(file) && service.length(file) > 0) { - thumbnailUri = Uri.fromFile(file); - } } finally { if (cursor != null) { cursor.close(); } } - if (thumbnailUri != null) { - try { - inputStream = contentResolver.openInputStream(thumbnailUri); - // openInputStream can throw NPEs. - } catch (NullPointerException e) { - throw (FileNotFoundException) - new FileNotFoundException("NPE opening uri: " + thumbnailUri).initCause(e); - } - } - return inputStream; + } + + private boolean isValid(File file) { + return service.exists(file) && 0 < service.length(file); } } diff --git a/library/src/main/java/com/bumptech/glide/load/engine/ActiveResources.java b/library/src/main/java/com/bumptech/glide/load/engine/ActiveResources.java index 19671164c0..5ccbf7b7f7 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/ActiveResources.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/ActiveResources.java @@ -38,8 +38,11 @@ public boolean handleMessage(Message msg) { private ResourceListener listener; - // Lazily instantiate to avoid exceptions if Glide is initialized on a background thread. See - // #295. + /** + * Lazily instantiate to avoid exceptions if Glide is initialized on a background thread. + * + * @see #295 + */ @Nullable private ReferenceQueue> resourceReferenceQueue; @Nullable diff --git a/library/src/main/java/com/bumptech/glide/load/engine/DataCacheGenerator.java b/library/src/main/java/com/bumptech/glide/load/engine/DataCacheGenerator.java index 0972f30ad5..bc19d18eb8 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/DataCacheGenerator.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/DataCacheGenerator.java @@ -51,6 +51,9 @@ public boolean startNext() { } Key sourceId = cacheKeys.get(sourceIdIndex); + // PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times + // and the actions it performs are much more expensive than a single allocation. + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") Key originalKey = new DataCacheKey(sourceId, helper.getSignature()); cacheFile = helper.getDiskCache().get(originalKey); if (cacheFile != null) { diff --git a/library/src/main/java/com/bumptech/glide/load/engine/Engine.java b/library/src/main/java/com/bumptech/glide/load/engine/Engine.java index 3bccb0f297..dcb320c2d8 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/Engine.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/Engine.java @@ -265,7 +265,6 @@ private EngineResource loadFromCache(Key key, boolean isMemoryCacheable) { return cached; } - @SuppressWarnings("unchecked") private EngineResource getEngineResourceFromCache(Key key) { Resource cached = cache.remove(key); @@ -394,8 +393,9 @@ public DiskCache getDiskCache() { @VisibleForTesting static class DecodeJobFactory { @Synthetic final DecodeJob.DiskCacheProvider diskCacheProvider; - @Synthetic final Pools.Pool> pool = FactoryPools.simple(JOB_POOL_SIZE, - new FactoryPools.Factory>() { + @Synthetic final Pools.Pool> pool = + FactoryPools.simple(JOB_POOL_SIZE, + new FactoryPools.Factory>() { @Override public DecodeJob create() { return new DecodeJob<>(diskCacheProvider, pool); @@ -453,19 +453,21 @@ static class EngineJobFactory { @Synthetic final GlideExecutor sourceUnlimitedExecutor; @Synthetic final GlideExecutor animationExecutor; @Synthetic final EngineJobListener listener; - @Synthetic final Pools.Pool> pool = FactoryPools.simple(JOB_POOL_SIZE, - new FactoryPools.Factory>() { - @Override - public EngineJob create() { - return new EngineJob<>( - diskCacheExecutor, - sourceExecutor, - sourceUnlimitedExecutor, - animationExecutor, - listener, - pool); - } - }); + @Synthetic final Pools.Pool> pool = + FactoryPools.simple( + JOB_POOL_SIZE, + new FactoryPools.Factory>() { + @Override + public EngineJob create() { + return new EngineJob<>( + diskCacheExecutor, + sourceExecutor, + sourceUnlimitedExecutor, + animationExecutor, + listener, + pool); + } + }); EngineJobFactory( GlideExecutor diskCacheExecutor, diff --git a/library/src/main/java/com/bumptech/glide/load/engine/LoadPath.java b/library/src/main/java/com/bumptech/glide/load/engine/LoadPath.java index ec15b8da45..fd50d328a1 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/LoadPath.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/LoadPath.java @@ -75,7 +75,6 @@ public Class getDataClass() { @Override public String toString() { - return "LoadPath{" + "decodePaths=" - + Arrays.toString(decodePaths.toArray(new DecodePath[decodePaths.size()])) + '}'; + return "LoadPath{" + "decodePaths=" + Arrays.toString(decodePaths.toArray()) + '}'; } } diff --git a/library/src/main/java/com/bumptech/glide/load/engine/ResourceCacheGenerator.java b/library/src/main/java/com/bumptech/glide/load/engine/ResourceCacheGenerator.java index 537da858a9..c30722bf82 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/ResourceCacheGenerator.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/ResourceCacheGenerator.java @@ -20,7 +20,7 @@ class ResourceCacheGenerator implements DataFetcherGenerator, private final FetcherReadyCallback cb; private final DecodeHelper helper; - private int sourceIdIndex = 0; + private int sourceIdIndex; private int resourceClassIndex = -1; private Key sourceKey; private List> modelLoaders; @@ -57,9 +57,11 @@ public boolean startNext() { Key sourceId = sourceIds.get(sourceIdIndex); Class resourceClass = resourceClasses.get(resourceClassIndex); Transformation transformation = helper.getTransformation(resourceClass); - + // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway, + // we only run until the first one succeeds, the loop runs for only a limited + // number of iterations on the order of 10-20 in the worst case. currentKey = - new ResourceCacheKey( + new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops helper.getArrayPool(), sourceId, helper.getSignature(), @@ -70,7 +72,7 @@ public boolean startNext() { helper.getOptions()); cacheFile = helper.getDiskCache().get(currentKey); if (cacheFile != null) { - this.sourceKey = sourceId; + sourceKey = sourceId; modelLoaders = helper.getModelLoaders(cacheFile); modelLoaderIndex = 0; } @@ -80,9 +82,8 @@ public boolean startNext() { boolean started = false; while (!started && hasNextModelLoader()) { ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++); - loadData = - modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), - helper.getOptions()); + loadData = modelLoader.buildLoadData(cacheFile, + helper.getWidth(), helper.getHeight(), helper.getOptions()); if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { started = true; loadData.fetcher.loadData(helper.getPriority(), this); diff --git a/library/src/main/java/com/bumptech/glide/load/engine/bitmap_recycle/SizeStrategy.java b/library/src/main/java/com/bumptech/glide/load/engine/bitmap_recycle/SizeStrategy.java index a5b89f53dd..5536b75db0 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/bitmap_recycle/SizeStrategy.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/bitmap_recycle/SizeStrategy.java @@ -7,7 +7,7 @@ import android.support.annotation.VisibleForTesting; import com.bumptech.glide.util.Synthetic; import com.bumptech.glide.util.Util; -import java.util.TreeMap; +import java.util.NavigableMap; /** * A strategy for reusing bitmaps that relies on @@ -16,11 +16,11 @@ *

Requires {@link Build.VERSION_CODES#KITKAT KitKat} or higher.

*/ @RequiresApi(Build.VERSION_CODES.KITKAT) -class SizeStrategy implements LruPoolStrategy { +final class SizeStrategy implements LruPoolStrategy { private static final int MAX_SIZE_MULTIPLE = 8; private final KeyPool keyPool = new KeyPool(); private final GroupedLinkedMap groupedMap = new GroupedLinkedMap<>(); - private final TreeMap sortedSizes = new PrettyPrintTreeMap<>(); + private final NavigableMap sortedSizes = new PrettyPrintTreeMap<>(); @Override public void put(Bitmap bitmap) { @@ -102,11 +102,11 @@ private static String getBitmapString(Bitmap bitmap) { return getBitmapString(size); } - @Synthetic - static String getBitmapString(int size) { + @Synthetic static String getBitmapString(int size) { return "[" + size + "]"; } + // Non-final for mocking. @VisibleForTesting static class KeyPool extends BaseKeyPool { diff --git a/library/src/main/java/com/bumptech/glide/load/engine/cache/DiskLruCacheWrapper.java b/library/src/main/java/com/bumptech/glide/load/engine/cache/DiskLruCacheWrapper.java index 364f842892..2cedfe0b29 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/cache/DiskLruCacheWrapper.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/cache/DiskLruCacheWrapper.java @@ -22,7 +22,7 @@ public class DiskLruCacheWrapper implements DiskCache { private static final int APP_VERSION = 1; private static final int VALUE_COUNT = 1; - private static DiskLruCacheWrapper wrapper = null; + private static DiskLruCacheWrapper wrapper; private final SafeKeyGenerator safeKeyGenerator; private final File directory; diff --git a/library/src/main/java/com/bumptech/glide/load/engine/prefill/BitmapPreFillRunner.java b/library/src/main/java/com/bumptech/glide/load/engine/prefill/BitmapPreFillRunner.java index 34272a47d9..a8c5d641c2 100644 --- a/library/src/main/java/com/bumptech/glide/load/engine/prefill/BitmapPreFillRunner.java +++ b/library/src/main/java/com/bumptech/glide/load/engine/prefill/BitmapPreFillRunner.java @@ -123,7 +123,12 @@ boolean allocate() { // Don't over fill the memory cache to avoid evicting useful resources, but make sure it's // not empty so that we use all available space. if (getFreeMemoryCacheBytes() >= bitmapSize) { - memoryCache.put(new UniqueKey(), BitmapResource.obtain(bitmap, bitmapPool)); + // We could probably make UniqueKey just always return false from equals, + // but the allocation of the Key is not nearly as expensive as the allocation of the Bitmap, + // so it's probably not worth it. + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + Key uniqueKey = new UniqueKey(); + memoryCache.put(uniqueKey, BitmapResource.obtain(bitmap, bitmapPool)); } else { bitmapPool.put(bitmap); } diff --git a/library/src/main/java/com/bumptech/glide/load/model/ByteArrayLoader.java b/library/src/main/java/com/bumptech/glide/load/model/ByteArrayLoader.java index 42b17bf235..bb8213fc1e 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/ByteArrayLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/ByteArrayLoader.java @@ -20,8 +20,7 @@ public class ByteArrayLoader implements ModelLoader { private final Converter converter; - // Public API. - @SuppressWarnings("WeakerAccess") + @SuppressWarnings("WeakerAccess") // Public API public ByteArrayLoader(Converter converter) { this.converter = converter; } @@ -39,10 +38,12 @@ public boolean handles(@NonNull byte[] model) { /** * Converts between a byte array a desired model class. + * * @param The type of data to convert to. */ public interface Converter { Data convert(byte[] model); + Class getDataClass(); } @@ -50,6 +51,12 @@ private static class Fetcher implements DataFetcher { private final byte[] model; private final Converter converter; + /** + * @param model We really ought to copy the model, but doing so can be hugely expensive and/or + * lead to OOMs. In practice it's unlikely that users would pass an array into + * Glide and then mutate it. + */ + @SuppressWarnings("PMD.ArrayIsStoredDirectly") Fetcher(byte[] model, Converter converter) { this.model = model; this.converter = converter; @@ -92,7 +99,7 @@ public static class ByteBufferFactory implements ModelLoaderFactory build(MultiModelLoaderFactory multiFactory) { + public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { return new ByteArrayLoader<>(new Converter() { @Override public ByteBuffer convert(byte[] model) { @@ -119,7 +126,7 @@ public static class StreamFactory implements ModelLoaderFactory build(MultiModelLoaderFactory multiFactory) { + public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { return new ByteArrayLoader<>(new Converter() { @Override public InputStream convert(byte[] model) { diff --git a/library/src/main/java/com/bumptech/glide/load/model/ByteBufferFileLoader.java b/library/src/main/java/com/bumptech/glide/load/model/ByteBufferFileLoader.java index 213c25ce18..c56d04b08b 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/ByteBufferFileLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/ByteBufferFileLoader.java @@ -37,7 +37,7 @@ public static class Factory implements ModelLoaderFactory { @NonNull @Override - public ModelLoader build(MultiModelLoaderFactory multiFactory) { + public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { return new ByteBufferFileLoader(); } @@ -47,7 +47,7 @@ public void teardown() { } } - private static class ByteBufferFetcher implements DataFetcher { + private static final class ByteBufferFetcher implements DataFetcher { private final File file; diff --git a/library/src/main/java/com/bumptech/glide/load/model/DataUrlLoader.java b/library/src/main/java/com/bumptech/glide/load/model/DataUrlLoader.java index 6865bb818e..b5bddccaff 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/DataUrlLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/DataUrlLoader.java @@ -152,12 +152,13 @@ public Class getDataClass() { @NonNull @Override - public final ModelLoader build(MultiModelLoaderFactory multiFactory) { + public ModelLoader build( + @NonNull MultiModelLoaderFactory multiFactory) { return new DataUrlLoader<>(opener); } @Override - public final void teardown() { + public void teardown() { // Do nothing. } } diff --git a/library/src/main/java/com/bumptech/glide/load/model/FileLoader.java b/library/src/main/java/com/bumptech/glide/load/model/FileLoader.java index f56f8bd589..e4f0d91781 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/FileLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/FileLoader.java @@ -52,7 +52,7 @@ public interface FileOpener { Class getDataClass(); } - private static class FileFetcher implements DataFetcher { + private static final class FileFetcher implements DataFetcher { private final File file; private final FileOpener opener; private Data data; @@ -118,7 +118,7 @@ public Factory(FileOpener opener) { @NonNull @Override - public final ModelLoader build(MultiModelLoaderFactory multiFactory) { + public final ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { return new FileLoader<>(opener); } diff --git a/library/src/main/java/com/bumptech/glide/load/model/LazyHeaders.java b/library/src/main/java/com/bumptech/glide/load/model/LazyHeaders.java index 3dbae2a516..e7f446ca56 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/LazyHeaders.java +++ b/library/src/main/java/com/bumptech/glide/load/model/LazyHeaders.java @@ -1,5 +1,6 @@ package com.bumptech.glide.load.model; +import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import java.util.ArrayList; @@ -42,28 +43,32 @@ private Map generateHeaders() { Map combinedHeaders = new HashMap<>(); for (Map.Entry> entry : headers.entrySet()) { - StringBuilder sb = new StringBuilder(); - List factories = entry.getValue(); - int size = factories.size(); - for (int i = 0; i < size; i++) { - LazyHeaderFactory factory = factories.get(i); - String header = factory.buildHeader(); - if (!TextUtils.isEmpty(header)) { - sb.append(header); - if (i != factories.size() - 1) { - sb.append(','); - } - } - } - String values = sb.toString(); + String values = buildHeaderValue(entry.getValue()); if (!TextUtils.isEmpty(values)) { - combinedHeaders.put(entry.getKey(), sb.toString()); + combinedHeaders.put(entry.getKey(), values); } } return combinedHeaders; } + @NonNull + private String buildHeaderValue(@NonNull List factories) { + StringBuilder sb = new StringBuilder(); + int size = factories.size(); + for (int i = 0; i < size; i++) { + LazyHeaderFactory factory = factories.get(i); + String header = factory.buildHeader(); + if (!TextUtils.isEmpty(header)) { + sb.append(header); + if (i != factories.size() - 1) { + sb.append(','); + } + } + } + return sb.toString(); + } + @Override public String toString() { return "LazyHeaders{" @@ -96,8 +101,6 @@ public int hashCode() { * {@link #addHeader(String, String)}, even though {@link #addHeader(String, LazyHeaderFactory)} * would usually append an additional value.

*/ - // PMD doesn't like the necessary static block to initialize DEFAULT_HEADERS. - @SuppressWarnings({"PMD.FieldDeclarationsShouldBeAtStartOfClass", "WeakerAccess"}) public static final class Builder { private static final String USER_AGENT_HEADER = "User-Agent"; private static final String DEFAULT_USER_AGENT = getSanitizedUserAgent(); @@ -128,7 +131,6 @@ public static final class Builder { * (i.e. an OAuth token).

* * @see #addHeader(String, LazyHeaderFactory) - */ public Builder addHeader(String key, String value) { return addHeader(key, new StringHeaderFactory(value)); @@ -163,8 +165,7 @@ public Builder addHeader(String key, LazyHeaderFactory factory) { *

Use {@link #setHeader(String, LazyHeaderFactory)} if obtaining the value requires I/O * (i.e. an OAuth token).

*/ - // Public API. - @SuppressWarnings("UnusedReturnValue") + @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) // Public API public Builder setHeader(String key, String value) { return setHeader(key, value == null ? null : new StringHeaderFactory(value)); } @@ -217,10 +218,11 @@ public LazyHeaders build() { } private Map> copyHeaders() { - Map> result = - new HashMap<>(headers.size()); + Map> result = new HashMap<>(headers.size()); for (Map.Entry> entry : headers.entrySet()) { - result.put(entry.getKey(), new ArrayList<>(entry.getValue())); + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + List valueCopy = new ArrayList<>(entry.getValue()); + result.put(entry.getKey(), valueCopy); } return result; } @@ -228,7 +230,7 @@ private Map> copyHeaders() { /** * Ensures that the default header will pass OkHttp3's checks for header values. * - *

See #2331. + * @see #2331 */ @VisibleForTesting static String getSanitizedUserAgent() { diff --git a/library/src/main/java/com/bumptech/glide/load/model/MultiModelLoader.java b/library/src/main/java/com/bumptech/glide/load/model/MultiModelLoader.java index 684dc76b00..443304fccb 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/MultiModelLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/MultiModelLoader.java @@ -68,8 +68,7 @@ public boolean handles(@NonNull Model model) { @Override public String toString() { - return "MultiModelLoader{" + "modelLoaders=" + Arrays - .toString(modelLoaders.toArray(new ModelLoader[modelLoaders.size()])) + '}'; + return "MultiModelLoader{" + "modelLoaders=" + Arrays.toString(modelLoaders.toArray()) + '}'; } static class MultiFetcher implements DataFetcher, DataCallback { diff --git a/library/src/main/java/com/bumptech/glide/load/model/StringLoader.java b/library/src/main/java/com/bumptech/glide/load/model/StringLoader.java index 9ce67c102d..16bb6cfde5 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/StringLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/StringLoader.java @@ -42,7 +42,8 @@ private static Uri parseUri(String model) { Uri uri; if (TextUtils.isEmpty(model)) { return null; - } else if (model.startsWith("/")) { + // See https://pmd.github.io/pmd-6.0.0/pmd_rules_java_performance.html#simplifystartswith + } else if (model.charAt(0) == '/') { uri = toFileUri(model); } else { uri = Uri.parse(model); diff --git a/library/src/main/java/com/bumptech/glide/load/model/UnitModelLoader.java b/library/src/main/java/com/bumptech/glide/load/model/UnitModelLoader.java index 4ca9dd16a7..670e869869 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/UnitModelLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/UnitModelLoader.java @@ -25,6 +25,8 @@ public static UnitModelLoader getInstance() { /** * @deprecated Use {@link #getInstance()} instead. */ + // Need constructor to document deprecation, will be removed, when constructor is privatized. + @SuppressWarnings({"PMD.UnnecessaryConstructor", "DeprecatedIsStillUsed"}) @Deprecated public UnitModelLoader() { // Intentionally empty. @@ -84,7 +86,7 @@ public DataSource getDataSource() { * * @param The type of model that will also be returned as decodable data. */ - // PMD seems to be just wrong here, maybe confused by getInstance in UnitModelLoader. + // PMD.SingleMethodSingleton false positive: https://github.com/pmd/pmd/issues/816 @SuppressWarnings("PMD.SingleMethodSingleton") public static class Factory implements ModelLoaderFactory { @SuppressWarnings("deprecation") @@ -96,6 +98,8 @@ public static Factory getInstance() { } /** @deprecated Use {@link #getInstance()} instead. */ + // Need constructor to document deprecation, will be removed, when constructor is privatized. + @SuppressWarnings("PMD.UnnecessaryConstructor") @Deprecated public Factory() { // Intentionally empty. diff --git a/library/src/main/java/com/bumptech/glide/load/model/stream/BaseGlideUrlLoader.java b/library/src/main/java/com/bumptech/glide/load/model/stream/BaseGlideUrlLoader.java index 68c606f00d..20c7657760 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/stream/BaseGlideUrlLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/stream/BaseGlideUrlLoader.java @@ -11,6 +11,7 @@ import com.bumptech.glide.load.model.ModelLoader; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -69,7 +70,9 @@ public LoadData buildLoadData(@NonNull Model model, int width, int } } - private static List getAlternateKeys(List alternateUrls) { + // Creating a limited number of objects as the sole purpose of the loop. + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + private static List getAlternateKeys(Collection alternateUrls) { List result = new ArrayList<>(alternateUrls.size()); for (String alternate : alternateUrls) { result.add(new GlideUrl(alternate)); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DefaultImageHeaderParser.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DefaultImageHeaderParser.java index 6a64cf121b..a2252613bc 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DefaultImageHeaderParser.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DefaultImageHeaderParser.java @@ -79,7 +79,7 @@ public int getOrientation(ByteBuffer byteBuffer, ArrayPool byteArrayPool) throws } private ImageType getType(Reader reader) throws IOException { - int firstTwoBytes = reader.getUInt16(); + final int firstTwoBytes = reader.getUInt16(); // JPEG. if (firstTwoBytes == EXIF_MAGIC_NUMBER) { @@ -208,10 +208,8 @@ private boolean hasJpegExifPreamble(byte[] exifData, int exifSegmentLength) { * {@code -1} if no exif segment is found. */ private int moveToExifSegmentAndGetLength(Reader reader) throws IOException { - short segmentId, segmentType; - int segmentLength; while (true) { - segmentId = reader.getUInt8(); + short segmentId = reader.getUInt8(); if (segmentId != SEGMENT_START_ID) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Unknown segmentId=" + segmentId); @@ -219,8 +217,7 @@ private int moveToExifSegmentAndGetLength(Reader reader) throws IOException { return -1; } - segmentType = reader.getUInt8(); - + short segmentType = reader.getUInt8(); if (segmentType == SEGMENT_SOS) { return -1; } else if (segmentType == MARKER_EOI) { @@ -231,8 +228,7 @@ private int moveToExifSegmentAndGetLength(Reader reader) throws IOException { } // Segment length includes bytes for segment length. - segmentLength = reader.getUInt16() - 2; - + int segmentLength = reader.getUInt16() - 2; if (segmentType != EXIF_SEGMENT_TYPE) { long skipped = reader.skip(segmentLength); if (skipped != segmentLength) { @@ -274,19 +270,16 @@ private static int parseExifSegment(RandomAccessReader segmentData) { int firstIfdOffset = segmentData.getInt32(headerOffsetSize + 4) + headerOffsetSize; int tagCount = segmentData.getInt16(firstIfdOffset); - - int tagOffset, tagType, formatCode, componentCount; for (int i = 0; i < tagCount; i++) { - tagOffset = calcTagOffset(firstIfdOffset, i); - tagType = segmentData.getInt16(tagOffset); + final int tagOffset = calcTagOffset(firstIfdOffset, i); + final int tagType = segmentData.getInt16(tagOffset); // We only want orientation. if (tagType != ORIENTATION_TAG_TYPE) { continue; } - formatCode = segmentData.getInt16(tagOffset + 2); - + final int formatCode = segmentData.getInt16(tagOffset + 2); // 12 is max format code. if (formatCode < 1 || formatCode > 12) { if (Log.isLoggable(TAG, Log.DEBUG)) { @@ -295,8 +288,7 @@ private static int parseExifSegment(RandomAccessReader segmentData) { continue; } - componentCount = segmentData.getInt32(tagOffset + 4); - + final int componentCount = segmentData.getInt32(tagOffset + 4); if (componentCount < 0) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Negative tiff component count"); @@ -310,7 +302,6 @@ private static int parseExifSegment(RandomAccessReader segmentData) { } final int byteCount = componentCount + BYTES_PER_FORMAT[formatCode]; - if (byteCount > 4) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Got byte count > 4, not orientation, continuing, formatCode=" + formatCode); @@ -319,7 +310,6 @@ private static int parseExifSegment(RandomAccessReader segmentData) { } final int tagValueOffset = tagOffset + 8; - if (tagValueOffset < 0 || tagValueOffset > segmentData.length()) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Illegal tagValueOffset=" + tagValueOffset + " tagType=" + tagType); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java index 9275e34297..dee51f7a2f 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java @@ -69,6 +69,7 @@ public Resource transform(@NonNull Context context, } } + // It's clearer to cast the result in a separate line from obtaining it. @SuppressWarnings({"unchecked", "PMD.UnnecessaryLocalBeforeReturn"}) private Resource newDrawableResource( Context context, Resource transformed) { diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java index 0a041908d6..aa21f9ce9b 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java @@ -368,15 +368,15 @@ public synchronized void reset() throws IOException { */ @Override public synchronized long skip(long byteCount) throws IOException { + if (byteCount < 1) { + return 0; + } // Use local refs since buf and in may be invalidated by an unsynchronized close() byte[] localBuf = buf; - InputStream localIn = in; if (localBuf == null) { throw streamClosed(); } - if (byteCount < 1) { - return 0; - } + InputStream localIn = in; if (localIn == null) { throw streamClosed(); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java index 16e61a0d62..8978e818ce 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java @@ -121,13 +121,16 @@ public static Bitmap centerCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitm } // From ImageView/Bitmap.createScaledBitmap. final float scale; - float dx = 0, dy = 0; + final float dx; + final float dy; Matrix m = new Matrix(); if (inBitmap.getWidth() * height > width * inBitmap.getHeight()) { scale = (float) height / (float) inBitmap.getHeight(); dx = (width - inBitmap.getWidth() * scale) * 0.5f; + dy = 0; } else { scale = (float) width / (float) inBitmap.getWidth(); + dx = 0; dy = (height - inBitmap.getHeight() * scale) * 0.5f; } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bytes/BytesResource.java b/library/src/main/java/com/bumptech/glide/load/resource/bytes/BytesResource.java index 6b8fde35e4..88068855d0 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bytes/BytesResource.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bytes/BytesResource.java @@ -20,8 +20,18 @@ public Class getResourceClass() { return byte[].class; } + /** + * In most cases it will only be retrieved once (see linked methods). + * + * @return the same array every time, do not mutate the contents. Not a copy returned, because + * copying the array can be prohibitively expensive and/or lead to OOMs. + * @see com.bumptech.glide.load.ResourceEncoder + * @see com.bumptech.glide.load.resource.transcode.ResourceTranscoder + * @see com.bumptech.glide.request.SingleRequest#onResourceReady + */ @NonNull @Override + @SuppressWarnings("PMD.MethodReturnsInternalArray") public byte[] get() { return bytes; } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/ByteBufferGifDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/ByteBufferGifDecoder.java index 6781b922ad..dbab1827cf 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/ByteBufferGifDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/ByteBufferGifDecoder.java @@ -92,34 +92,36 @@ public GifDrawableResource decode(@NonNull ByteBuffer source, int width, int hei private GifDrawableResource decode( ByteBuffer byteBuffer, int width, int height, GifHeaderParser parser, Options options) { long startTime = LogTime.getLogTime(); - final GifHeader header = parser.parseHeader(); - if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) { - // If we couldn't decode the GIF, we will end up with a frame count of 0. - return null; - } + try { + final GifHeader header = parser.parseHeader(); + if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) { + // If we couldn't decode the GIF, we will end up with a frame count of 0. + return null; + } - Bitmap.Config config = options.get(GifOptions.DECODE_FORMAT) == DecodeFormat.PREFER_RGB_565 - ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888; + Bitmap.Config config = options.get(GifOptions.DECODE_FORMAT) == DecodeFormat.PREFER_RGB_565 + ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888; - int sampleSize = getSampleSize(header, width, height); - GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize); - gifDecoder.setDefaultBitmapConfig(config); - gifDecoder.advance(); - Bitmap firstFrame = gifDecoder.getNextFrame(); - if (firstFrame == null) { - return null; - } + int sampleSize = getSampleSize(header, width, height); + GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize); + gifDecoder.setDefaultBitmapConfig(config); + gifDecoder.advance(); + Bitmap firstFrame = gifDecoder.getNextFrame(); + if (firstFrame == null) { + return null; + } - Transformation unitTransformation = UnitTransformation.get(); + Transformation unitTransformation = UnitTransformation.get(); - GifDrawable gifDrawable = - new GifDrawable(context, gifDecoder, unitTransformation, width, height, firstFrame); + GifDrawable gifDrawable = + new GifDrawable(context, gifDecoder, unitTransformation, width, height, firstFrame); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime)); + return new GifDrawableResource(gifDrawable); + } finally { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime)); + } } - - return new GifDrawableResource(gifDrawable); } private static int getSampleSize(GifHeader gifHeader, int targetWidth, int targetHeight) { diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java index bee286b776..c46775bd2b 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java @@ -37,9 +37,9 @@ class GifFrameLoader { @SuppressWarnings("WeakerAccess") @Synthetic final RequestManager requestManager; private final BitmapPool bitmapPool; - private boolean isRunning = false; - private boolean isLoadPending = false; - private boolean startFromFirstFrame = false; + private boolean isRunning; + private boolean isLoadPending; + private boolean startFromFirstFrame; private RequestBuilder requestBuilder; private DelayTarget current; private boolean isCleared; @@ -111,10 +111,10 @@ void subscribe(FrameCallback frameCallback) { if (isCleared) { throw new IllegalStateException("Cannot subscribe to a cleared frame loader"); } - boolean start = callbacks.isEmpty(); if (callbacks.contains(frameCallback)) { throw new IllegalStateException("Cannot subscribe twice in a row"); } + boolean start = callbacks.isEmpty(); callbacks.add(frameCallback); if (start) { start(); diff --git a/library/src/main/java/com/bumptech/glide/request/RequestOptions.java b/library/src/main/java/com/bumptech/glide/request/RequestOptions.java index 9bf9b5205a..3dc929ef2b 100644 --- a/library/src/main/java/com/bumptech/glide/request/RequestOptions.java +++ b/library/src/main/java/com/bumptech/glide/request/RequestOptions.java @@ -810,7 +810,11 @@ public RequestOptions signature(@NonNull Key signature) { *

Even if this object was locked, the cloned object returned from this method will not be * locked.

*/ - @SuppressWarnings("unchecked") + @SuppressWarnings({ + "unchecked", + // we don't want to throw to be user friendly + "PMD.CloneThrowsCloneNotSupportedException" + }) @CheckResult @Override public RequestOptions clone() { @@ -1647,14 +1651,20 @@ private boolean isSet(int flag) { return isSet(fields, flag); } + // get is just as clear. + @SuppressWarnings("PMD.BooleanGetMethodName") public final boolean getUseUnlimitedSourceGeneratorsPool() { return useUnlimitedSourceGeneratorsPool; } + // get is just as clear. + @SuppressWarnings("PMD.BooleanGetMethodName") public final boolean getUseAnimationPool() { return useAnimationPool; } + // get is just as clear. + @SuppressWarnings("PMD.BooleanGetMethodName") public final boolean getOnlyRetrieveFromCache() { return onlyRetrieveFromCache; } diff --git a/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java b/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java index be500d87ca..635b6b5d08 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java @@ -43,8 +43,8 @@ */ public abstract class ViewTarget extends BaseTarget { private static final String TAG = "ViewTarget"; - private static boolean isTagUsedAtLeastOnce = false; - @Nullable private static Integer tagId = null; + private static boolean isTagUsedAtLeastOnce; + @Nullable private static Integer tagId; protected final T view; private final SizeDeterminer sizeDeterminer; @@ -74,8 +74,7 @@ public ViewTarget(@NonNull T view) { * * @deprecated Use {@link #waitForLayout()} instead. */ - // Public API. - @SuppressWarnings("WeakerAccess") + @SuppressWarnings("WeakerAccess") // Public API @Deprecated public ViewTarget(@NonNull T view, boolean waitForLayout) { this(view); @@ -161,7 +160,7 @@ public void onViewDetachedFromWindow(View v) { * still be used instead of the {@link View}'s dimensions even if this method is called. This * parameter is a fallback only. */ - @SuppressWarnings("WeakerAccess") + @SuppressWarnings("WeakerAccess") // Public API @NonNull public final ViewTarget waitForLayout() { sizeDeterminer.waitForLayout = true; diff --git a/library/src/main/java/com/bumptech/glide/signature/ApplicationVersionSignature.java b/library/src/main/java/com/bumptech/glide/signature/ApplicationVersionSignature.java index 2bcd360c06..21d86931d9 100644 --- a/library/src/main/java/com/bumptech/glide/signature/ApplicationVersionSignature.java +++ b/library/src/main/java/com/bumptech/glide/signature/ApplicationVersionSignature.java @@ -4,18 +4,21 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; +import android.util.Log; import com.bumptech.glide.load.Key; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * A utility class for obtaining a {@link com.bumptech.glide.load.Key} signature containing the * application version name using {@link android.content.pm.PackageInfo#versionCode}. */ public final class ApplicationVersionSignature { - private static final ConcurrentHashMap PACKAGE_NAME_TO_KEY = - new ConcurrentHashMap<>(); + private static final String TAG = "AppVersionSignature"; + private static final ConcurrentMap PACKAGE_NAME_TO_KEY = new ConcurrentHashMap<>(); /** * Returns the signature {@link com.bumptech.glide.load.Key} for version code of the Application @@ -44,20 +47,30 @@ static void reset() { @NonNull private static Key obtainVersionSignature(@NonNull Context context) { - PackageInfo pInfo = null; - try { - pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - // Should never happen. - e.printStackTrace(); - } - final String versionCode; - if (pInfo != null) { - versionCode = String.valueOf(pInfo.versionCode); + PackageInfo packageInfo = getPackageInfo(context); + String versionCode = getVersionCode(packageInfo); + return new ObjectKey(versionCode); + } + + @NonNull + private static String getVersionCode(@Nullable PackageInfo packageInfo) { + String versionCode; + if (packageInfo != null) { + versionCode = String.valueOf(packageInfo.versionCode); } else { versionCode = UUID.randomUUID().toString(); } - return new ObjectKey(versionCode); + return versionCode; + } + + @Nullable + private static PackageInfo getPackageInfo(@NonNull Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Cannot resolve info for" + context.getPackageName(), e); + return null; + } } private ApplicationVersionSignature() { diff --git a/library/src/main/java/com/bumptech/glide/util/ByteBufferUtil.java b/library/src/main/java/com/bumptech/glide/util/ByteBufferUtil.java index e1b73aa342..8f0bee837b 100644 --- a/library/src/main/java/com/bumptech/glide/util/ByteBufferUtil.java +++ b/library/src/main/java/com/bumptech/glide/util/ByteBufferUtil.java @@ -15,8 +15,7 @@ /** * Utilities for interacting with {@link java.nio.ByteBuffer}s. */ -// Public API. -@SuppressWarnings({"unused", "WeakerAccess"}) +@SuppressWarnings({"unused", "WeakerAccess"}) // Public API public final class ByteBufferUtil { // 16 Kb private static final int BUFFER_SIZE = 16384; @@ -167,6 +166,8 @@ static final class SafeArray { @Synthetic final int limit; @Synthetic final byte[] data; + // PMD.ArrayIsStoredDirectly Copying would be prohibitively expensive and/or lead to OOMs. + @SuppressWarnings("PMD.ArrayIsStoredDirectly") SafeArray(@NonNull byte[] data, int offset, int limit) { this.data = data; this.offset = offset; @@ -207,7 +208,7 @@ public boolean markSupported() { } @Override - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { + public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException { if (!byteBuffer.hasRemaining()) { return -1; } diff --git a/library/src/main/java/com/bumptech/glide/util/FixedPreloadSizeProvider.java b/library/src/main/java/com/bumptech/glide/util/FixedPreloadSizeProvider.java index 92daaf11ba..08b7ed3ba5 100644 --- a/library/src/main/java/com/bumptech/glide/util/FixedPreloadSizeProvider.java +++ b/library/src/main/java/com/bumptech/glide/util/FixedPreloadSizeProvider.java @@ -25,6 +25,9 @@ public FixedPreloadSizeProvider(int width, int height) { @Nullable @Override + // It's better to take on the risk that callers may mutate the array when there isn't any reason + // for them to do so than it the performance overhead of copying the array with every call. + @SuppressWarnings("PMD.MethodReturnsInternalArray") public int[] getPreloadSize(@NonNull T item, int adapterPosition, int itemPosition) { return size; } diff --git a/library/src/main/java/com/bumptech/glide/util/LruCache.java b/library/src/main/java/com/bumptech/glide/util/LruCache.java index 5f7402aea2..a2cd7d8223 100644 --- a/library/src/main/java/com/bumptech/glide/util/LruCache.java +++ b/library/src/main/java/com/bumptech/glide/util/LruCache.java @@ -15,10 +15,10 @@ * @param The type of the values. */ public class LruCache { - private final LinkedHashMap cache = new LinkedHashMap<>(100, 0.75f, true); + private final Map cache = new LinkedHashMap<>(100, 0.75f, true); private final long initialMaxSize; private long maxSize; - private long currentSize = 0; + private long currentSize; /** * Constructor for LruCache. diff --git a/library/src/main/java/com/bumptech/glide/util/Util.java b/library/src/main/java/com/bumptech/glide/util/Util.java index 7e294e2c79..b5cae5cbad 100644 --- a/library/src/main/java/com/bumptech/glide/util/Util.java +++ b/library/src/main/java/com/bumptech/glide/util/Util.java @@ -81,7 +81,7 @@ public static int getBitmapByteSize(@NonNull Bitmap bitmap) { // Workaround for KitKat initial release NPE in Bitmap, fixed in MR1. See issue #148. try { return bitmap.getAllocationByteCount(); - } catch (NullPointerException e) { + } catch (@SuppressWarnings("PMD.AvoidCatchingNPE") NullPointerException e) { // Do nothing. } } diff --git a/library/src/test/java/com/bumptech/glide/signature/ApplicationVersionSignatureTest.java b/library/src/test/java/com/bumptech/glide/signature/ApplicationVersionSignatureTest.java index 2757c0873d..0d2c90bb85 100644 --- a/library/src/test/java/com/bumptech/glide/signature/ApplicationVersionSignatureTest.java +++ b/library/src/test/java/com/bumptech/glide/signature/ApplicationVersionSignatureTest.java @@ -1,8 +1,11 @@ package com.bumptech.glide.signature; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; import com.bumptech.glide.load.Key; import com.bumptech.glide.tests.KeyTester; import java.io.UnsupportedEncodingException; @@ -12,6 +15,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -51,4 +55,29 @@ public void testKeyForSignatureIsTheSameAcrossCallsInTheSamePackage() "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9") .test(); } + + @Test + public void testUnresolvablePackageInfo() throws NameNotFoundException { + Context context = mock(Context.class, Answers.RETURNS_DEEP_STUBS.get()); + String packageName = "my.package"; + when(context.getPackageName()).thenReturn(packageName); + when(context.getPackageManager().getPackageInfo(packageName, 0)) + .thenThrow(new NameNotFoundException("test")); + + Key key = ApplicationVersionSignature.obtain(context); + + assertNotNull(key); + } + + @Test + public void testMissingPackageInfo() throws NameNotFoundException { + Context context = mock(Context.class, Answers.RETURNS_DEEP_STUBS.get()); + String packageName = "my.package"; + when(context.getPackageName()).thenReturn(packageName); + when(context.getPackageManager().getPackageInfo(packageName, 0)).thenReturn(null); + + Key key = ApplicationVersionSignature.obtain(context); + + assertNotNull(key); + } }