@nativescript-community/ui-image 5.0.12 → 5.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/index-common.d.ts +30 -1
- package/index-common.js.map +1 -1
- package/index.android.d.ts +11 -4
- package/index.android.js +159 -71
- package/index.android.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.ios.d.ts +3 -3
- package/index.ios.js +6 -18
- package/index.ios.js.map +1 -1
- package/package.json +2 -2
- package/platforms/android/java/com/nativescript/image/CacheKeyStore.java +15 -27
- package/platforms/android/java/com/nativescript/image/CustomGlideModule.java +35 -154
- package/platforms/android/java/com/nativescript/image/CustomGlideUrl.java +2 -2
- package/platforms/android/java/com/nativescript/image/EvictionManager.java +331 -333
- package/platforms/android/java/com/nativescript/image/GlideConfiguration.java +28 -0
- package/platforms/android/java/com/nativescript/image/ModelSignatureDiskLruCacheWrapper.java +321 -0
- package/platforms/android/java/com/nativescript/image/ModelSignatureMemoryCache.java +155 -0
- package/platforms/android/java/com/nativescript/image/SaveKeysRequestListener.java +69 -125
- package/platforms/android/java/com/nativescript/image/SharedPrefCacheKeyStore.java +75 -49
- package/typings/ui_image.android.d.ts +10 -17
- package/platforms/android/java/com/nativescript/image/CapturingEngineKeyFactory.java +0 -43
- package/platforms/android/java/com/nativescript/image/RecordingDigest.java +0 -48
- package/platforms/android/java/com/nativescript/image/RecreatedResourceKey.java +0 -95
|
@@ -8,8 +8,10 @@ package com.nativescript.image;
|
|
|
8
8
|
public class GlideConfiguration {
|
|
9
9
|
private static GlideConfiguration instance;
|
|
10
10
|
|
|
11
|
+
private long diskCacheSize = 250 * 1024 * 1024;
|
|
11
12
|
private long memoryCacheSize = -1; // -1 means use default
|
|
12
13
|
private float memoryCacheScreens = 2.0f;
|
|
14
|
+
private String diskCacheName = null;
|
|
13
15
|
|
|
14
16
|
private GlideConfiguration() {
|
|
15
17
|
}
|
|
@@ -21,6 +23,19 @@ public class GlideConfiguration {
|
|
|
21
23
|
return instance;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Set custom disk cache name.
|
|
28
|
+
* Must be called before Glide is initialized (before first image load).
|
|
29
|
+
* @param name String
|
|
30
|
+
*/
|
|
31
|
+
public void setDiskCacheName(String diskCacheName) {
|
|
32
|
+
this.diskCacheName = diskCacheName;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public String getDiskCacheName() {
|
|
36
|
+
return diskCacheName;
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
/**
|
|
25
40
|
* Set custom memory cache size in bytes.
|
|
26
41
|
* Must be called before Glide is initialized (before first image load).
|
|
@@ -34,6 +49,19 @@ public class GlideConfiguration {
|
|
|
34
49
|
return memoryCacheSize;
|
|
35
50
|
}
|
|
36
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Set custom disk cache size in bytes.
|
|
54
|
+
* Must be called before Glide is initialized (before first image load).
|
|
55
|
+
* @param bytes Size in bytes
|
|
56
|
+
*/
|
|
57
|
+
public void setDiskCacheSize(long bytes) {
|
|
58
|
+
this.diskCacheSize = bytes;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public long getDiskCacheSize() {
|
|
62
|
+
return diskCacheSize;
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
/**
|
|
38
66
|
* Set memory cache screens multiplier (default 2.0).
|
|
39
67
|
* Only used if memoryCacheSize is -1.
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
package com.bumptech.glide.load.engine.cache;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
import androidx.annotation.NonNull;
|
|
5
|
+
import androidx.annotation.Nullable;
|
|
6
|
+
import com.bumptech.glide.disklrucache.DiskLruCache;
|
|
7
|
+
import com.bumptech.glide.disklrucache.DiskLruCache.Value;
|
|
8
|
+
import com.bumptech.glide.load.Key;
|
|
9
|
+
import java.io.File;
|
|
10
|
+
import java.io.IOException;
|
|
11
|
+
import java.util.ArrayList;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
import java.util.Map;
|
|
14
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A disk cache wrapper that tracks cache keys for efficient querying and eviction based on
|
|
18
|
+
* model/signature pairs. Similar to ModelSignatureMemoryCache but for disk cache.
|
|
19
|
+
*
|
|
20
|
+
* Features:
|
|
21
|
+
* - Tracks all cached keys with their string representations
|
|
22
|
+
* - Supports querying existence by model and signature
|
|
23
|
+
* - Supports bulk eviction by model/signature or signature version
|
|
24
|
+
* - Thread-safe operations with minimal overhead
|
|
25
|
+
*/
|
|
26
|
+
public class ModelSignatureDiskLruCacheWrapper implements DiskCache {
|
|
27
|
+
private static final String TAG = "JS";
|
|
28
|
+
|
|
29
|
+
private static final int APP_VERSION = 1;
|
|
30
|
+
private static final int VALUE_COUNT = 1;
|
|
31
|
+
private static ModelSignatureDiskLruCacheWrapper wrapper;
|
|
32
|
+
|
|
33
|
+
private final SafeKeyGenerator safeKeyGenerator;
|
|
34
|
+
private final File directory;
|
|
35
|
+
private final long maxSize;
|
|
36
|
+
private final DiskCacheWriteLocker writeLocker = new DiskCacheWriteLocker();
|
|
37
|
+
private DiskLruCache diskLruCache;
|
|
38
|
+
|
|
39
|
+
// Index of safeKey -> original key string for efficient lookup
|
|
40
|
+
// This allows us to query and evict based on model/signature without iterating the entire cache
|
|
41
|
+
private final Map<String, String> keyIndex = new ConcurrentHashMap<>();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get a DiskCache in the given directory and size. If a disk cache has already been created with
|
|
45
|
+
* a different directory and/or size, it will be returned instead and the new arguments will be
|
|
46
|
+
* ignored.
|
|
47
|
+
*
|
|
48
|
+
* @param directory The directory for the disk cache
|
|
49
|
+
* @param maxSize The max size for the disk cache
|
|
50
|
+
* @return The new disk cache with the given arguments, or the current cache if one already exists
|
|
51
|
+
* @deprecated Use {@link #create(File, long)} to create a new cache with the specified arguments.
|
|
52
|
+
*/
|
|
53
|
+
@SuppressWarnings("deprecation")
|
|
54
|
+
@Deprecated
|
|
55
|
+
public static synchronized DiskCache get(File directory, long maxSize) {
|
|
56
|
+
if (wrapper == null) {
|
|
57
|
+
wrapper = new ModelSignatureDiskLruCacheWrapper(directory, maxSize);
|
|
58
|
+
}
|
|
59
|
+
return wrapper;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a new DiskCache in the given directory with a specified max size.
|
|
64
|
+
*
|
|
65
|
+
* @param directory The directory for the disk cache
|
|
66
|
+
* @param maxSize The max size for the disk cache
|
|
67
|
+
* @return The new disk cache with the given arguments
|
|
68
|
+
*/
|
|
69
|
+
@SuppressWarnings("deprecation")
|
|
70
|
+
public static ModelSignatureDiskLruCacheWrapper create(File directory, long maxSize) {
|
|
71
|
+
return new ModelSignatureDiskLruCacheWrapper(directory, maxSize);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Do not extend this class.
|
|
76
|
+
*/
|
|
77
|
+
@Deprecated
|
|
78
|
+
// Deprecated public API.
|
|
79
|
+
@SuppressWarnings({"WeakerAccess", "DeprecatedIsStillUsed"})
|
|
80
|
+
protected ModelSignatureDiskLruCacheWrapper(File directory, long maxSize) {
|
|
81
|
+
this.directory = directory;
|
|
82
|
+
this.maxSize = maxSize;
|
|
83
|
+
this.safeKeyGenerator = new SafeKeyGenerator();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private synchronized DiskLruCache getDiskCache() throws IOException {
|
|
87
|
+
if (diskLruCache == null) {
|
|
88
|
+
diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
|
|
89
|
+
}
|
|
90
|
+
return diskLruCache;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@Override
|
|
94
|
+
public File get(Key key) {
|
|
95
|
+
String safeKey = safeKeyGenerator.getSafeKey(key);
|
|
96
|
+
File result = null;
|
|
97
|
+
try {
|
|
98
|
+
// It is possible that the there will be a put in between these two gets. If so that shouldn't
|
|
99
|
+
// be a problem because we will always put the same value at the same key so our input streams
|
|
100
|
+
// will still represent the same data.
|
|
101
|
+
final DiskLruCache.Value value = getDiskCache().get(safeKey);
|
|
102
|
+
// Log.v(TAG, "DiskCache Get: " + safeKey + " for Key: " + key + " found: " + value);
|
|
103
|
+
if (value != null) {
|
|
104
|
+
result = value.getFile(0);
|
|
105
|
+
}
|
|
106
|
+
} catch (IOException e) {
|
|
107
|
+
if (Log.isLoggable(TAG, Log.WARN)) {
|
|
108
|
+
Log.w(TAG, "Unable to get from disk cache", e);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@Override
|
|
115
|
+
public void put(Key key, Writer writer) {
|
|
116
|
+
// We want to make sure that puts block so that data is available when put completes. We may
|
|
117
|
+
// actually not write any data if we find that data is written by the time we acquire the lock.
|
|
118
|
+
String safeKey = safeKeyGenerator.getSafeKey(key);
|
|
119
|
+
writeLocker.acquire(safeKey);
|
|
120
|
+
try {
|
|
121
|
+
try {
|
|
122
|
+
// We assume we only need to put once, so if data was written while we were trying to get
|
|
123
|
+
// the lock, we can simply abort.
|
|
124
|
+
DiskLruCache diskCache = getDiskCache();
|
|
125
|
+
Value current = diskCache.get(safeKey);
|
|
126
|
+
if (current != null) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
DiskLruCache.Editor editor = diskCache.edit(safeKey);
|
|
131
|
+
if (editor == null) {
|
|
132
|
+
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
File file = editor.getFile(0);
|
|
136
|
+
if (writer.write(file)) {
|
|
137
|
+
editor.commit();
|
|
138
|
+
// Track the key in our index after successful write
|
|
139
|
+
keyIndex.put(safeKey, key.toString());
|
|
140
|
+
// Log.w(TAG, "DiskCache Put: " + safeKey + " for Key: " + key + " size: " + keyIndex.size());
|
|
141
|
+
}
|
|
142
|
+
} finally {
|
|
143
|
+
editor.abortUnlessCommitted();
|
|
144
|
+
}
|
|
145
|
+
} catch (IOException e) {
|
|
146
|
+
if (Log.isLoggable(TAG, Log.WARN)) {
|
|
147
|
+
Log.w(TAG, "Unable to put to disk cache", e);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} finally {
|
|
151
|
+
writeLocker.release(safeKey);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@Override
|
|
156
|
+
public void delete(Key key) {
|
|
157
|
+
String safeKey = safeKeyGenerator.getSafeKey(key);
|
|
158
|
+
try {
|
|
159
|
+
getDiskCache().remove(safeKey);
|
|
160
|
+
// Remove from index
|
|
161
|
+
keyIndex.remove(safeKey);
|
|
162
|
+
// Log.w(TAG, "DiskCache deleted: " + safeKey + " size: " + keyIndex.size());
|
|
163
|
+
} catch (IOException e) {
|
|
164
|
+
if (Log.isLoggable(TAG, Log.WARN)) {
|
|
165
|
+
Log.w(TAG, "Unable to delete from disk cache", e);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@Override
|
|
171
|
+
public synchronized void clear() {
|
|
172
|
+
try {
|
|
173
|
+
getDiskCache().delete();
|
|
174
|
+
// Clear the index
|
|
175
|
+
keyIndex.clear();
|
|
176
|
+
} catch (IOException e) {
|
|
177
|
+
Log.w(TAG, "Unable to clear disk cache or disk cache cleared externally", e);
|
|
178
|
+
} finally {
|
|
179
|
+
// Delete can close the cache but still throw. If we don't null out the disk cache here, every
|
|
180
|
+
// subsequent request will try to act on a closed disk cache and fail. By nulling out the disk
|
|
181
|
+
// cache we at least allow for attempts to open the cache in the future. See #2465.
|
|
182
|
+
resetDiskCache();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private synchronized void resetDiskCache() {
|
|
187
|
+
diskLruCache = null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if an entry exists in the cache by model and signature.
|
|
192
|
+
* This is more efficient than trying to get the actual file.
|
|
193
|
+
*
|
|
194
|
+
* @param model The model (e.g., URI)
|
|
195
|
+
* @param signature The signature Key
|
|
196
|
+
* @return true if at least one matching entry exists
|
|
197
|
+
*/
|
|
198
|
+
public boolean containsByModelAndSignature(@NonNull Object model, @NonNull Key signature) {
|
|
199
|
+
final String modelStr = model.toString();
|
|
200
|
+
final String signatureStr = signature.toString();
|
|
201
|
+
|
|
202
|
+
for (String keyStr : keyIndex.values()) {
|
|
203
|
+
if (matchesModelAndSignature(keyStr, modelStr, signatureStr)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Remove all entries matching the given model and signature.
|
|
212
|
+
* This removes both raw (source) and transformed (resource) cache entries.
|
|
213
|
+
*
|
|
214
|
+
* @param model The model to match
|
|
215
|
+
* @param signature The signature to match
|
|
216
|
+
* @return Number of entries removed
|
|
217
|
+
*/
|
|
218
|
+
public int removeByModelAndSignature(@NonNull Object model, @NonNull Key signature) {
|
|
219
|
+
final String modelStr = model.toString();
|
|
220
|
+
final String signatureStr = signature.toString();
|
|
221
|
+
|
|
222
|
+
// Log.w(TAG, "DiskCache removeByModelAndSignature: model=" + modelStr + " signature=" + signatureStr + " cacheCount=" + keyIndex.size());
|
|
223
|
+
|
|
224
|
+
List<String> safeKeysToRemove = new ArrayList<>();
|
|
225
|
+
|
|
226
|
+
// Find all matching keys
|
|
227
|
+
for (Map.Entry<String, String> entry : keyIndex.entrySet()) {
|
|
228
|
+
String keyStr = entry.getValue();
|
|
229
|
+
if (matchesModelAndSignature(keyStr, modelStr, signatureStr)) {
|
|
230
|
+
safeKeysToRemove.add(entry.getKey());
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Remove them from disk cache
|
|
235
|
+
int removed = 0;
|
|
236
|
+
for (String safeKey : safeKeysToRemove) {
|
|
237
|
+
try {
|
|
238
|
+
getDiskCache().remove(safeKey);
|
|
239
|
+
keyIndex.remove(safeKey);
|
|
240
|
+
removed++;
|
|
241
|
+
// Log.v(TAG, "DiskCache removed: " + safeKey);
|
|
242
|
+
} catch (IOException e) {
|
|
243
|
+
Log.w(TAG, "Failed to remove disk cache entry: " + safeKey, e);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Log.w(TAG, "DiskCache removed " + removed + " entries for model/signature");
|
|
248
|
+
return removed;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Remove all entries that do NOT match the given signature.
|
|
253
|
+
* Useful for cache invalidation when changing signature versions (e.g., v1 -> v2).
|
|
254
|
+
*
|
|
255
|
+
* @param currentSignature The current/valid signature to keep
|
|
256
|
+
* @return Number of entries removed
|
|
257
|
+
*/
|
|
258
|
+
public int removeAllExceptSignature(@NonNull Key currentSignature) {
|
|
259
|
+
final String signatureStr = currentSignature.toString();
|
|
260
|
+
|
|
261
|
+
List<String> safeKeysToRemove = new ArrayList<>();
|
|
262
|
+
|
|
263
|
+
// Find all keys that don't match the current signature
|
|
264
|
+
for (Map.Entry<String, String> entry : keyIndex.entrySet()) {
|
|
265
|
+
String keyStr = entry.getValue();
|
|
266
|
+
if (!keyStr.contains(signatureStr) && !keyStr.contains("signature=" + signatureStr)) {
|
|
267
|
+
safeKeysToRemove.add(entry.getKey());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Remove them
|
|
272
|
+
int removed = 0;
|
|
273
|
+
for (String safeKey : safeKeysToRemove) {
|
|
274
|
+
try {
|
|
275
|
+
getDiskCache().remove(safeKey);
|
|
276
|
+
keyIndex.remove(safeKey);
|
|
277
|
+
removed++;
|
|
278
|
+
} catch (IOException e) {
|
|
279
|
+
Log.w(TAG, "Failed to remove disk cache entry: " + safeKey, e);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Log.i(TAG, "DiskCache removed " + removed + " entries with different signatures");
|
|
284
|
+
return removed;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get statistics about the cache index.
|
|
289
|
+
*
|
|
290
|
+
* @return Array: [indexSize, diskCacheSize]
|
|
291
|
+
*/
|
|
292
|
+
public long[] getStats() {
|
|
293
|
+
long indexSize = keyIndex.size();
|
|
294
|
+
long diskSize = 0;
|
|
295
|
+
try {
|
|
296
|
+
diskSize = getDiskCache().size();
|
|
297
|
+
} catch (IOException e) {
|
|
298
|
+
Log.w(TAG, "Failed to get disk cache size", e);
|
|
299
|
+
}
|
|
300
|
+
return new long[] { indexSize, diskSize };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Helper method to check if a key string matches a model and signature.
|
|
305
|
+
* Uses multiple matching strategies for robustness.
|
|
306
|
+
*/
|
|
307
|
+
private boolean matchesModelAndSignature(String keyStr, String modelStr, String signatureStr) {
|
|
308
|
+
// Strategy 1: Exact pattern matching for known key formats (e.g., DataCacheKey, ResourceCacheKey)
|
|
309
|
+
if (keyStr.contains("model=" + modelStr) && keyStr.contains("signature=" + signatureStr)) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Strategy 2: Substring matching (fallback for other key formats)
|
|
314
|
+
// Only match if both model and signature are present
|
|
315
|
+
if (keyStr.contains(modelStr) && keyStr.contains(signatureStr)) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
import com.bumptech.glide.load.Key;
|
|
7
|
+
import com.bumptech.glide.load.engine.Resource;
|
|
8
|
+
import com.bumptech.glide.load.engine.cache.LruResourceCache;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import java.util.Map;
|
|
13
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
14
|
+
import java.util.Arrays;
|
|
15
|
+
|
|
16
|
+
import android.util.Log;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An LRU memory cache that keeps a small index of the string form of each Key so callers can
|
|
20
|
+
* evict all entries matching a model + signature pair.
|
|
21
|
+
*/
|
|
22
|
+
public final class ModelSignatureMemoryCache extends LruResourceCache {
|
|
23
|
+
private static final String TAG = "JS";
|
|
24
|
+
|
|
25
|
+
// Map of the Key -> cached key string (avoid calling toString() repeatedly and to have a quick
|
|
26
|
+
// snapshot of what's in cache). Entries are added on put and removed on remove / eviction.
|
|
27
|
+
private final Map<Key, String> keyStrings = new ConcurrentHashMap<>();
|
|
28
|
+
|
|
29
|
+
public ModelSignatureMemoryCache(long maxSizeBytes) {
|
|
30
|
+
super(maxSizeBytes);
|
|
31
|
+
}
|
|
32
|
+
@Override
|
|
33
|
+
public void trimMemory(int level) {
|
|
34
|
+
super.trimMemory(level);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Nullable
|
|
38
|
+
@Override
|
|
39
|
+
public Resource<?> put(@NonNull Key key, @Nullable Resource<?> resource) {
|
|
40
|
+
// Save string representation for indexing before delegating.
|
|
41
|
+
keyStrings.put(key, key.toString());
|
|
42
|
+
// Log.w(TAG, "MemoryCache put " + key.toString() + " " + keyStrings.size());
|
|
43
|
+
return super.put(key, resource);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Nullable
|
|
47
|
+
@Override
|
|
48
|
+
public Resource<?> remove(@NonNull Key key) {
|
|
49
|
+
// Log.w(TAG, "MemoryCache remove " + key + " " + keyStrings.size());
|
|
50
|
+
keyStrings.remove(key);
|
|
51
|
+
return super.remove(key);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
protected void onItemEvicted(@NonNull Key key, @Nullable Resource<?> item) {
|
|
56
|
+
// Called by the LRU when an item is evicted; keep our index in sync.
|
|
57
|
+
keyStrings.remove(key);
|
|
58
|
+
super.onItemEvicted(key, item);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove all entries whose underlying Key looks like it was created for the given model and
|
|
63
|
+
* signature. Both checks are substring based against Key.toString().
|
|
64
|
+
*
|
|
65
|
+
* @param model The model (for example the URI or whatever you passed to .load(...)).
|
|
66
|
+
* The code uses model.toString() to match keys.
|
|
67
|
+
* @param signature The request signature (a Key, e.g. new ObjectKey(...)) as used in the
|
|
68
|
+
* original request. Uses signature.toString() to match keys.
|
|
69
|
+
* @return number of removed entries
|
|
70
|
+
*/
|
|
71
|
+
public int removeByModelAndSignature(@NonNull Object model, @NonNull Key signature) {
|
|
72
|
+
final String modelStr = model.toString();
|
|
73
|
+
final String modelCleanStr = (modelStr.startsWith("ObjectKey{object=") && modelStr.endsWith("}")) ? modelStr.substring(17, modelStr.length() - 1) : null;
|
|
74
|
+
final String signatureStr = signature.toString();
|
|
75
|
+
|
|
76
|
+
List<Key> toRemove = new ArrayList<>();
|
|
77
|
+
for (Map.Entry<Key, String> e : keyStrings.entrySet()) {
|
|
78
|
+
String s = e.getValue();
|
|
79
|
+
// There are two checks: a direct "model=" / "signature=" pattern check (matches EngineKey.toString
|
|
80
|
+
// format currently used), and a fallback that simply checks both substrings exist. The first
|
|
81
|
+
// reduces false positives.
|
|
82
|
+
if (((s.contains("model=" + modelStr) || (modelCleanStr != null && s.contains("model=" + modelCleanStr)))&& s.contains("signature=" + signatureStr))) {
|
|
83
|
+
toRemove.add(e.getKey());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Log.w(TAG, "ModelSignatureMemoryCache removeByModelAndSignature will remove " + toRemove.size() + " items from " + keyStrings.size() );
|
|
87
|
+
|
|
88
|
+
for (Key k : toRemove) {
|
|
89
|
+
remove(k);
|
|
90
|
+
}
|
|
91
|
+
return toRemove.size();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Convenience method if you only have a model string (e.g. a URI) and a signature string.
|
|
96
|
+
*
|
|
97
|
+
* This is less type-safe (signatures should be Key), but may be helpful in some cases.
|
|
98
|
+
*/
|
|
99
|
+
public int removeByModelAndSignatureStrings(@NonNull String modelStr, @NonNull String signatureStr) {
|
|
100
|
+
List<Key> toRemove = new ArrayList<>();
|
|
101
|
+
final String modelCleanStr = (modelStr.startsWith("ObjectKey{object=") && modelStr.endsWith("}")) ? modelStr.substring(17, modelStr.length() - 1) : null;
|
|
102
|
+
for (Map.Entry<Key, String> e : keyStrings.entrySet()) {
|
|
103
|
+
String s = e.getValue();
|
|
104
|
+
if (((s.contains("model=" + modelStr) || (modelCleanStr != null && s.contains("model=" + modelCleanStr)))&& s.contains("signature=" + signatureStr))) {
|
|
105
|
+
toRemove.add(e.getKey());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (Key k : toRemove) {
|
|
109
|
+
remove(k);
|
|
110
|
+
}
|
|
111
|
+
return toRemove.size();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Test whether there is at least one entry in the memory cache that matches the given
|
|
116
|
+
* model and signature.
|
|
117
|
+
*
|
|
118
|
+
* Matching is performed by checking the cached Key.toString() for substrings derived from the
|
|
119
|
+
* provided model and signature (same heuristic used by removeByModelAndSignature).
|
|
120
|
+
*
|
|
121
|
+
* @param model The model object used in the original request (e.g. the URI or model instance).
|
|
122
|
+
* @param signature The signature Key used in the original request.
|
|
123
|
+
* @return true if at least one matching entry exists in memory cache.
|
|
124
|
+
*/
|
|
125
|
+
public boolean containsByModelAndSignature(@NonNull Object model, @NonNull Key signature) {
|
|
126
|
+
final String modelStr = model.toString();
|
|
127
|
+
final String modelCleanStr = (modelStr.startsWith("ObjectKey{object=") && modelStr.endsWith("}")) ? modelStr.substring(17, modelStr.length() - 1) : null;
|
|
128
|
+
final String signatureStr = signature.toString();
|
|
129
|
+
// Log.i(TAG, "ModelSignatureMemoryCache containsByModelAndSignature " + modelStr + " " + signatureStr + " " + keyStrings.size());
|
|
130
|
+
|
|
131
|
+
for (String s : keyStrings.values()) {
|
|
132
|
+
if (((s.contains("model=" + modelStr) || (modelCleanStr != null && s.contains("model=" + modelCleanStr)))&& s.contains("signature=" + signatureStr))) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Convenience version that accepts precomputed strings for model and signature.
|
|
141
|
+
*
|
|
142
|
+
* @param modelString model.toString() or similar representation.
|
|
143
|
+
* @param signatureString signature.toString() or similar representation.
|
|
144
|
+
* @return true if at least one matching entry exists in memory cache.
|
|
145
|
+
*/
|
|
146
|
+
public boolean containsByModelAndSignatureStrings(@NonNull String modelStr, @NonNull String signatureStr) {
|
|
147
|
+
final String modelCleanStr = (modelStr.startsWith("ObjectKey{object=") && modelStr.endsWith("}")) ? modelStr.substring(17, modelStr.length() - 1) : null;
|
|
148
|
+
for (String s : keyStrings.values()) {
|
|
149
|
+
if (((s.contains("model=" + modelStr) || (modelCleanStr != null && s.contains("model=" + modelCleanStr)))&& s.contains("signature=" + signatureStr))) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|