diff --git a/.travis.yml b/.travis.yml index 9c0daec..5e33e9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,6 @@ before_script: - adb wait-for-device && sleep 300 - adb shell input keyevent 82 & -script: ./gradlew lib:connectedAndroidTest -Ptravis --stacktrace \ No newline at end of file +script: + - ./gradlew lib:connectedAndroidTest -Ptravis --stacktrace + - ./gradlew lib:check -Ptravis --stacktrace diff --git a/config/quality.gradle b/config/quality.gradle index ff219a2..ff8218d 100644 --- a/config/quality.gradle +++ b/config/quality.gradle @@ -16,7 +16,7 @@ task checkstyle(type: Checkstyle) { } -task findbugs(type: FindBugs) { +task findbugs(type: FindBugs, dependsOn: assembleDebug) { ignoreFailures = false effort = "max" reportLevel = "high" diff --git a/config/quality/checkstyle/suppressions.xml b/config/quality/checkstyle/suppressions.xml index 6fd5ec9..372f57a 100644 --- a/config/quality/checkstyle/suppressions.xml +++ b/config/quality/checkstyle/suppressions.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/config/quality/pmd/pmd-ruleset.xml b/config/quality/pmd/pmd-ruleset.xml index 4e007f7..35a5492 100644 --- a/config/quality/pmd/pmd-ruleset.xml +++ b/config/quality/pmd/pmd-ruleset.xml @@ -1,4 +1,20 @@ + + .*/R.java .*/gen/.* + .*/CustomLruCache.java - + - - - - - - - - + + + + + + + + + + + @@ -30,4 +50,4 @@ - \ No newline at end of file + diff --git a/gradle.properties b/gradle.properties index f9f3893..fe09183 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=2.1.0 +VERSION_NAME=2.2.0 VERSION_CODE=1 GROUP=com.vincentbrison.openlibraries.android diff --git a/lib/build.gradle b/lib/build.gradle index f4a565c..f6b9fdd 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -26,7 +26,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:22.2.0' compile 'com.jakewharton:disklrucache:2.0.2' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.2' diff --git a/lib/maven_push.gradle b/lib/maven_push.gradle index 8d7fefc..da9096d 100644 --- a/lib/maven_push.gradle +++ b/lib/maven_push.gradle @@ -74,7 +74,8 @@ afterEvaluate { project -> allprojects { ext."signing.password" = ask("Please provide your key password") ext."signing.keyId" = "D376F8F6" - ext."signing.secretKeyRingFile" = "/Users/vincentbrison/.gnupg/secring.gpg" + ext."signing.secretKeyRingFile" = "C:/Users/Brize/AppData/Roaming/gnupg/secring.gpg" + //ext."signing.secretKeyRingFile" = "/Users/vincentbrison/.gnupg/secring.gpg" } required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } sign configurations.archives diff --git a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/CustomLruCache.java b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/CustomLruCache.java new file mode 100644 index 0000000..58b9d26 --- /dev/null +++ b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/CustomLruCache.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.vincentbrison.openlibraries.android.dualcache.lib; + +import java.lang.reflect.InvocationTargetException; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A cache that holds strong references to a limited number of values. Each time + * a value is accessed, it is moved to the head of a queue. When a value is + * added to a full cache, the value at the end of that queue is evicted and may + * become eligible for garbage collection. + * + *

If your cached values hold resources that need to be explicitly released, + * override {@link #entryRemoved}. + * + *

If a cache miss should be computed on demand for the corresponding keys, + * override {@link #create}. This simplifies the calling code, allowing it to + * assume a value will always be returned, even when there's a cache miss. + * + *

By default, the cache size is measured in the number of entries. Override + * {@link #sizeOf} to size the cache in different units. For example, this cache + * is limited to 4MiB of bitmaps: + *

   {@code
+ *   int cacheSize = 4 * 1024 * 1024; // 4MiB
+ *   LruCache bitmapCache = new LruCache(cacheSize) {
+ *       protected int sizeOf(String key, Bitmap value) {
+ *           return value.getByteCount();
+ *       }
+ *   }}
+ * + *

This class is thread-safe. Perform multiple cache operations atomically by + * synchronizing on the cache:

   {@code
+ *   synchronized (cache) {
+ *     if (cache.get(key) == null) {
+ *         cache.put(key, value);
+ *     }
+ *   }}
+ * + *

This class does not allow null to be used as a key or value. A return + * value of null from {@link #get}, {@link #put} or {@link #remove} is + * unambiguous: the key was not in the cache. + * + *

This class appeared in Android 3.1 (Honeycomb MR1); it's available as part + * of Android's + * Support Package for earlier releases. + */ +public class CustomLruCache { + private final LinkedHashMap map; + + /** Size of this cache in units. Not necessarily the number of elements. */ + private int size; + private int maxSize; + + private int putCount; + private int createCount; + private int evictionCount; + private int hitCount; + private int missCount; + + /** + * @param maxSize for caches that do not override {@link #sizeOf}, this is + * the maximum number of entries in the cache. For all other caches, + * this is the maximum sum of the sizes of the entries in this cache. + */ + public CustomLruCache(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + this.maxSize = maxSize; + this.map = new LinkedHashMap(0, 0.75f, true); + } + + /** + * Sets the size of the cache. + * + * @param maxSize The new maximum size. + */ + public void resize(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + + synchronized (this) { + this.maxSize = maxSize; + } + trimToSize(maxSize); + } + + /** + * Returns the value for {@code key} if it exists in the cache or can be + * created by {@code #create}. If a value was returned, it is moved to the + * head of the queue. This returns null if a value is not cached and cannot + * be created. + */ + public final V get(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V mapValue; + synchronized (this) { + mapValue = map.get(key); + if (mapValue != null) { + hitCount++; + return mapValue; + } + missCount++; + } + + /* + * Attempt to create a value. This may take a long time, and the map + * may be different when create() returns. If a conflicting value was + * added to the map while create() was working, we leave that value in + * the map and release the created value. + */ + + V createdValue = create(key); + if (createdValue == null) { + return null; + } + + synchronized (this) { + createCount++; + mapValue = map.put(key, createdValue); + + if (mapValue != null) { + // There was a conflict so undo that last put + map.put(key, mapValue); + } else { + size += safeSizeOf(key, createdValue); + } + } + + if (mapValue != null) { + entryRemoved(false, key, createdValue, mapValue); + return mapValue; + } else { + trimToSize(maxSize); + return createdValue; + } + } + + /** + * Caches {@code value} for {@code key}. The value is moved to the head of + * the queue. + * + * @return the previous value mapped by {@code key}. + */ + public final V put(K key, V value) { + if (key == null || value == null) { + throw new NullPointerException("key == null || value == null"); + } + + V previous; + synchronized (this) { + putCount++; + size += safeSizeOf(key, value); + previous = map.put(key, value); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + } + + if (previous != null) { + entryRemoved(false, key, previous, value); + } + + trimToSize(maxSize); + return previous; + } + + /** + * Remove the eldest entries until the total of remaining entries is at or + * below the requested size. + * + * @param maxSize the maximum size of the cache before returning. May be -1 + * to evict even 0-sized elements. + */ + public void trimToSize(int maxSize) { + while (true) { + K key; + V value; + synchronized (this) { + if (size < 0 || (map.isEmpty() && size != 0)) { + throw new IllegalStateException(getClass().getName() + + ".sizeOf() is reporting inconsistent results!"); + } + + if (size <= maxSize) { + break; + } + Map.Entry toEvict = null; + try { + toEvict = (Map.Entry) map.getClass().getMethod("eldest").invoke(map); + + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + if (toEvict == null) { + break; + } + key = toEvict.getKey(); + value = toEvict.getValue(); + map.remove(key); + size -= safeSizeOf(key, value); + evictionCount++; + } + entryRemoved(true, key, value, null); + } + } + + /** + * Removes the entry for {@code key} if it exists. + * + * @return the previous value mapped by {@code key}. + */ + public final V remove(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V previous; + synchronized (this) { + previous = map.remove(key); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + } + + if (previous != null) { + entryRemoved(false, key, previous, null); + } + + return previous; + } + + /** + * Called for entries that have been evicted or removed. This method is + * invoked when a value is evicted to make space, removed by a call to + * {@link #remove}, or replaced by a call to {@link #put}. The default + * implementation does nothing. + * + *

The method is called without synchronization: other threads may + * access the cache while this method is executing. + * + * @param evicted true if the entry is being removed to make space, false + * if the removal was caused by a {@link #put} or {@link #remove}. + * @param newValue the new value for {@code key}, if it exists. If non-null, + * this removal was caused by a {@link #put}. Otherwise it was caused by + * an eviction or a {@link #remove}. + */ + protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} + + /** + * Called after a cache miss to compute a value for the corresponding key. + * Returns the computed value or null if no value can be computed. The + * default implementation returns null. + * + *

The method is called without synchronization: other threads may + * access the cache while this method is executing. + * + *

If a value for {@code key} exists in the cache when this method + * returns, the created value will be released with {@link #entryRemoved} + * and discarded. This can occur when multiple threads request the same key + * at the same time (causing multiple values to be created), or when one + * thread calls {@link #put} while another is creating a value for the same + * key. + */ + protected V create(K key) { + return null; + } + + private int safeSizeOf(K key, V value) { + int result = sizeOf(key, value); + if (result < 0) { + throw new IllegalStateException("Negative size: " + key + "=" + value); + } + return result; + } + + /** + * Returns the size of the entry for {@code key} and {@code value} in + * user-defined units. The default implementation returns 1 so that size + * is the number of entries and max size is the maximum number of entries. + * + *

An entry's size must not change while it is in the cache. + */ + protected int sizeOf(K key, V value) { + return 1; + } + + /** + * Clear the cache, calling {@link #entryRemoved} on each removed entry. + */ + public final void evictAll() { + trimToSize(-1); // -1 will evict 0-sized elements + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the number + * of entries in the cache. For all other caches, this returns the sum of + * the sizes of the entries in this cache. + */ + public synchronized final int size() { + return size; + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the maximum + * number of entries in the cache. For all other caches, this returns the + * maximum sum of the sizes of the entries in this cache. + */ + public synchronized final int maxSize() { + return maxSize; + } + + /** + * Returns the number of times {@link #get} returned a value that was + * already present in the cache. + */ + public synchronized final int hitCount() { + return hitCount; + } + + /** + * Returns the number of times {@link #get} returned null or required a new + * value to be created. + */ + public synchronized final int missCount() { + return missCount; + } + + /** + * Returns the number of times {@link #create(Object)} returned a value. + */ + public synchronized final int createCount() { + return createCount; + } + + /** + * Returns the number of times {@link #put} was called. + */ + public synchronized final int putCount() { + return putCount; + } + + /** + * Returns the number of values that have been evicted. + */ + public synchronized final int evictionCount() { + return evictionCount; + } + + /** + * Returns a copy of the current contents of the cache, ordered from least + * recently accessed to most recently accessed. + */ + public synchronized final Map snapshot() { + return new LinkedHashMap(map); + } + + @Override public synchronized final String toString() { + int accesses = hitCount + missCount; + int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; + return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", + maxSize, hitCount, missCount, hitPercent); + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCache.java b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCache.java index dd26a35..0567984 100755 --- a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCache.java +++ b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCache.java @@ -16,8 +16,6 @@ package com.vincentbrison.openlibraries.android.dualcache.lib; -import android.support.v4.util.LruCache; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonProcessingException; @@ -105,7 +103,7 @@ public enum DualCacheDiskMode { /** * RAM cache. */ - private LruCache mRamCacheLru; + private CustomLruCache mRamCacheLru; /** * Disk cache. @@ -193,7 +191,7 @@ protected void setDiskSerializer(Serializer diskSerializer) { mDiskSerializer = diskSerializer; } - protected void setRamCacheLru(LruCache ramLruCache) { + protected void setRamCacheLru(CustomLruCache ramLruCache) { mRamCacheLru = ramLruCache; } diff --git a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCacheDiskBuilder.java b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCacheDiskBuilder.java index 2180bff..14b43ee 100755 --- a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCacheDiskBuilder.java +++ b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/DualCacheDiskBuilder.java @@ -78,22 +78,23 @@ private File getDefaultDiskCacheFolder(boolean usePrivateFiles) { } private DualCache useCustomSerializerInDiskIfProvided(int maxDiskSize, File diskCacheFolder, Serializer serializer) { + File crtDiskCacheFolder = diskCacheFolder; mDualCache.setDiskCacheSizeInBytes(maxDiskSize); - if(serializer == null) { + if (serializer == null) { mDualCache.setDiskMode(DualCache.DualCacheDiskMode.ENABLE_WITH_DEFAULT_SERIALIZER); } else { mDualCache.setDiskMode(DualCache.DualCacheDiskMode.ENABLE_WITH_CUSTOM_SERIALIZER); mDualCache.setDiskSerializer(serializer); } - if(diskCacheFolder == null) { - diskCacheFolder = getDefaultDiskCacheFolder(false); + if (crtDiskCacheFolder == null) { + crtDiskCacheFolder = getDefaultDiskCacheFolder(false); } try { - mDualCache.setDiskLruCache(DiskLruCache.open(diskCacheFolder, mDualCache.getAppVersion(), 1, maxDiskSize)); - mDualCache.setDiskCacheFolder(diskCacheFolder); + mDualCache.setDiskLruCache(DiskLruCache.open(crtDiskCacheFolder, mDualCache.getAppVersion(), 1, maxDiskSize)); + mDualCache.setDiskCacheFolder(crtDiskCacheFolder); } catch (IOException e) { DualCacheLogUtils.logError(e); } diff --git a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/ReferenceLRUCache.java b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/ReferenceLRUCache.java index 8111d8a..2f8ec3a 100644 --- a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/ReferenceLRUCache.java +++ b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/ReferenceLRUCache.java @@ -1,12 +1,10 @@ package com.vincentbrison.openlibraries.android.dualcache.lib; -import android.support.v4.util.LruCache; - /** * This is the LRU cache used for the RAM layer when configured to used references. * @param is the class of object stored in the cache. */ -public class ReferenceLRUCache extends LruCache { +public class ReferenceLRUCache extends CustomLruCache { private SizeOf mHandlerSizeOf; diff --git a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/StringLRUCache.java b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/StringLRUCache.java index 49b17a3..f1d40b4 100644 --- a/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/StringLRUCache.java +++ b/lib/src/main/java/com/vincentbrison/openlibraries/android/dualcache/lib/StringLRUCache.java @@ -16,15 +16,13 @@ package com.vincentbrison.openlibraries.android.dualcache.lib; -import android.support.v4.util.LruCache; - import java.nio.charset.Charset; /** * LRU cache used by the RAM cache layer when storing serialized object. */ -public class StringLRUCache extends LruCache { +public class StringLRUCache extends CustomLruCache { /** * @param maxSize for caches that do not override {@link #sizeOf}, this is