@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
|
@@ -5,12 +5,15 @@ import android.os.Looper;
|
|
|
5
5
|
import androidx.annotation.GuardedBy;
|
|
6
6
|
import androidx.annotation.MainThread;
|
|
7
7
|
import androidx.annotation.Nullable;
|
|
8
|
+
import com.bumptech.glide.Glide;
|
|
8
9
|
import com.bumptech.glide.load.Key;
|
|
9
10
|
import com.bumptech.glide.load.Options;
|
|
10
11
|
import com.bumptech.glide.load.Transformation;
|
|
11
12
|
import com.bumptech.glide.load.engine.cache.DiskCache;
|
|
12
13
|
import com.bumptech.glide.load.engine.cache.MemoryCache;
|
|
13
14
|
import com.bumptech.glide.load.engine.cache.LruResourceCache;
|
|
15
|
+
import com.bumptech.glide.load.engine.cache.ModelSignatureDiskLruCacheWrapper;
|
|
16
|
+
|
|
14
17
|
import java.util.concurrent.ExecutorService;
|
|
15
18
|
import java.util.concurrent.Executors;
|
|
16
19
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
@@ -35,18 +38,29 @@ import android.util.Log;
|
|
|
35
38
|
* - Callbacks are always posted on the main thread.
|
|
36
39
|
*/
|
|
37
40
|
public final class EvictionManager {
|
|
38
|
-
private static final String TAG = "
|
|
41
|
+
private static final String TAG = "JS";
|
|
42
|
+
|
|
39
43
|
private static final EvictionManager INSTANCE = new EvictionManager();
|
|
40
44
|
|
|
45
|
+
// Cached reflection fields/methods for ActiveResources access
|
|
46
|
+
private static volatile java.lang.reflect.Field engineField;
|
|
47
|
+
private static volatile java.lang.reflect.Field activeResourcesField;
|
|
48
|
+
private static volatile java.lang.reflect.Field activeEngineResourcesField;
|
|
49
|
+
|
|
50
|
+
private static volatile boolean reflectionInitialized = false;
|
|
51
|
+
private static volatile boolean reflectionFailed = false;
|
|
52
|
+
|
|
41
53
|
private final ExecutorService diskExecutor = Executors.newSingleThreadExecutor();
|
|
42
54
|
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
43
55
|
|
|
44
56
|
@GuardedBy("this")
|
|
45
|
-
private
|
|
57
|
+
private ModelSignatureDiskLruCacheWrapper diskCache;
|
|
46
58
|
@GuardedBy("this")
|
|
47
|
-
private
|
|
59
|
+
private ModelSignatureMemoryCache memoryCache;
|
|
60
|
+
|
|
61
|
+
private Glide glideInstance;
|
|
48
62
|
|
|
49
|
-
// in-memory store for captured info
|
|
63
|
+
// in-memory store for captured info
|
|
50
64
|
private final CacheKeyStore inMemoryKeyStore = new CacheKeyStore();
|
|
51
65
|
|
|
52
66
|
// optional persistent store (SharedPref). If null, fallback to in-memory only.
|
|
@@ -60,11 +74,15 @@ public final class EvictionManager {
|
|
|
60
74
|
return INSTANCE;
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
public
|
|
77
|
+
public void setGlideInstance(Glide glideInstance) {
|
|
78
|
+
this.glideInstance = glideInstance;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public synchronized void setDiskCache(ModelSignatureDiskLruCacheWrapper diskCache) {
|
|
64
82
|
this.diskCache = diskCache;
|
|
65
83
|
}
|
|
66
84
|
|
|
67
|
-
public synchronized void setMemoryCache(
|
|
85
|
+
public synchronized void setMemoryCache(ModelSignatureMemoryCache memoryCache) {
|
|
68
86
|
this.memoryCache = memoryCache;
|
|
69
87
|
}
|
|
70
88
|
|
|
@@ -73,10 +91,12 @@ public final class EvictionManager {
|
|
|
73
91
|
}
|
|
74
92
|
|
|
75
93
|
/**
|
|
76
|
-
* Expose the in-memory store so callers/listeners can put
|
|
77
|
-
* there.
|
|
94
|
+
* Expose the in-memory store so callers/listeners can put key objects there.
|
|
78
95
|
*/
|
|
79
96
|
public CacheKeyStore getKeyStore() {
|
|
97
|
+
if (persistentStore != null) {
|
|
98
|
+
return persistentStore;
|
|
99
|
+
}
|
|
80
100
|
return inMemoryKeyStore;
|
|
81
101
|
}
|
|
82
102
|
|
|
@@ -85,45 +105,23 @@ public final class EvictionManager {
|
|
|
85
105
|
*/
|
|
86
106
|
public synchronized void saveKeys(final String id, final CacheKeyStore.StoredKeys newStored) {
|
|
87
107
|
if (id == null || newStored == null) {
|
|
88
|
-
Log.w(TAG, "saveKeys called with null id or newStored");
|
|
89
108
|
return;
|
|
90
109
|
}
|
|
91
110
|
|
|
92
111
|
try {
|
|
93
|
-
// 1) Load existing stored keys
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (persistentStore != null) {
|
|
112
|
+
// 1) Load existing stored keys
|
|
113
|
+
CacheKeyStore.StoredKeys existing = inMemoryKeyStore.get(id);
|
|
114
|
+
if (existing == null && persistentStore != null) {
|
|
97
115
|
existing = persistentStore.get(id);
|
|
98
116
|
}
|
|
99
|
-
if (existing == null) {
|
|
100
|
-
existing = inMemoryKeyStore.get(id);
|
|
101
|
-
}
|
|
102
117
|
|
|
103
|
-
// 2)
|
|
118
|
+
// 2) Just save the new keys directly
|
|
104
119
|
CacheKeyStore.StoredKeys toPersist = newStored;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
toPersist = new CacheKeyStore.StoredKeys(
|
|
109
|
-
toPersist.sourceKey,
|
|
110
|
-
toPersist.signature,
|
|
111
|
-
toPersist.width,
|
|
112
|
-
toPersist.height,
|
|
113
|
-
toPersist.transformation,
|
|
114
|
-
toPersist.transformationKeyBytes,
|
|
115
|
-
toPersist.decodedResourceClass,
|
|
116
|
-
toPersist.options,
|
|
117
|
-
toPersist.optionsKeyBytes,
|
|
118
|
-
existing.engineKey // preserve captured engine key
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 3) Persist to persistent store if available, else to in-memory store
|
|
120
|
+
// Log.d(TAG, "EvictionManager saveKeys " + id + " " + toPersist);
|
|
121
|
+
// 3) Save to both stores
|
|
122
|
+
inMemoryKeyStore.put(id, toPersist);
|
|
123
123
|
if (persistentStore != null) {
|
|
124
124
|
persistentStore.put(id, toPersist);
|
|
125
|
-
} else {
|
|
126
|
-
inMemoryKeyStore.put(id, toPersist);
|
|
127
125
|
}
|
|
128
126
|
} catch (Throwable t) {
|
|
129
127
|
Log.w(TAG, "saveKeys failed for id=" + id, t);
|
|
@@ -142,13 +140,9 @@ public final class EvictionManager {
|
|
|
142
140
|
|
|
143
141
|
/** Disk presence callback for async check. */
|
|
144
142
|
public interface DiskPresenceCallback {
|
|
145
|
-
void onResult(boolean
|
|
143
|
+
void onResult(boolean present);
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
// -----------------------
|
|
149
|
-
// New: clear APIs
|
|
150
|
-
// -----------------------
|
|
151
|
-
|
|
152
146
|
/**
|
|
153
147
|
* Clear the entire disk cache. Runs on a background thread. Callback invoked on
|
|
154
148
|
* main thread.
|
|
@@ -157,7 +151,7 @@ public final class EvictionManager {
|
|
|
157
151
|
diskExecutor.execute(() -> {
|
|
158
152
|
boolean success = true;
|
|
159
153
|
Exception ex = null;
|
|
160
|
-
|
|
154
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
161
155
|
synchronized (EvictionManager.this) {
|
|
162
156
|
dc = diskCache;
|
|
163
157
|
}
|
|
@@ -169,6 +163,9 @@ public final class EvictionManager {
|
|
|
169
163
|
ex = e;
|
|
170
164
|
}
|
|
171
165
|
}
|
|
166
|
+
if (persistentStore != null) {
|
|
167
|
+
persistentStore.clearAll();
|
|
168
|
+
}
|
|
172
169
|
if (callback != null) {
|
|
173
170
|
final boolean finalSuccess = success;
|
|
174
171
|
final Exception finalEx = ex;
|
|
@@ -191,18 +188,20 @@ public final class EvictionManager {
|
|
|
191
188
|
mainHandler.post(() -> {
|
|
192
189
|
boolean success = true;
|
|
193
190
|
Exception ex = null;
|
|
194
|
-
|
|
191
|
+
ModelSignatureMemoryCache mc;
|
|
195
192
|
synchronized (EvictionManager.this) {
|
|
196
193
|
mc = memoryCache;
|
|
197
194
|
}
|
|
198
195
|
if (mc != null) {
|
|
199
196
|
try {
|
|
200
197
|
mc.clearMemory();
|
|
198
|
+
clearAllActiveResources();
|
|
201
199
|
} catch (Exception e) {
|
|
202
200
|
success = false;
|
|
203
201
|
ex = e;
|
|
204
202
|
}
|
|
205
203
|
}
|
|
204
|
+
inMemoryKeyStore.clearAll();
|
|
206
205
|
if (callback != null) {
|
|
207
206
|
callback.onComplete(success, ex);
|
|
208
207
|
}
|
|
@@ -225,7 +224,7 @@ public final class EvictionManager {
|
|
|
225
224
|
diskExecutor.execute(() -> {
|
|
226
225
|
boolean diskSuccess = true;
|
|
227
226
|
Exception firstEx = null;
|
|
228
|
-
|
|
227
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
229
228
|
synchronized (EvictionManager.this) {
|
|
230
229
|
dc = diskCache;
|
|
231
230
|
}
|
|
@@ -242,13 +241,14 @@ public final class EvictionManager {
|
|
|
242
241
|
mainHandler.post(() -> {
|
|
243
242
|
boolean memSuccess = true;
|
|
244
243
|
Exception memEx = null;
|
|
245
|
-
|
|
244
|
+
ModelSignatureMemoryCache mc;
|
|
246
245
|
synchronized (EvictionManager.this) {
|
|
247
246
|
mc = memoryCache;
|
|
248
247
|
}
|
|
249
248
|
if (mc != null) {
|
|
250
249
|
try {
|
|
251
250
|
mc.clearMemory();
|
|
251
|
+
clearAllActiveResources();
|
|
252
252
|
} catch (Exception e) {
|
|
253
253
|
memSuccess = false;
|
|
254
254
|
if (finalFirstEx == null)
|
|
@@ -257,6 +257,10 @@ public final class EvictionManager {
|
|
|
257
257
|
}
|
|
258
258
|
boolean combined = finalDiskSuccess && memSuccess;
|
|
259
259
|
Exception combinedEx = finalFirstEx != null ? finalFirstEx : memEx;
|
|
260
|
+
inMemoryKeyStore.clearAll();
|
|
261
|
+
if (persistentStore != null) {
|
|
262
|
+
persistentStore.clearAll();
|
|
263
|
+
}
|
|
260
264
|
if (callback != null)
|
|
261
265
|
callback.onComplete(combined, combinedEx);
|
|
262
266
|
});
|
|
@@ -268,10 +272,6 @@ public final class EvictionManager {
|
|
|
268
272
|
clearAll(null);
|
|
269
273
|
}
|
|
270
274
|
|
|
271
|
-
// -----------------------
|
|
272
|
-
// Eviction-by-id APIs
|
|
273
|
-
// -----------------------
|
|
274
|
-
|
|
275
275
|
/**
|
|
276
276
|
* Evict both disk (source + transformed) and memory for id. Callback invoked on
|
|
277
277
|
* main thread.
|
|
@@ -279,22 +279,70 @@ public final class EvictionManager {
|
|
|
279
279
|
public void evictAllForId(final String id, @Nullable final EvictionCallback callback) {
|
|
280
280
|
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
281
281
|
if (s == null) {
|
|
282
|
-
// fallback conservative attempt (
|
|
283
|
-
evictDiskForId(id,
|
|
282
|
+
// fallback conservative attempt (disk only, then memory)
|
|
283
|
+
evictDiskForId(id, new EvictionCallback() {
|
|
284
|
+
@Override
|
|
285
|
+
public void onComplete(boolean diskSuccess, @Nullable Exception diskError) {
|
|
286
|
+
// After disk eviction, try memory eviction (will fail gracefully if no keys)
|
|
287
|
+
mainHandler.post(() -> {
|
|
288
|
+
evictMemoryForId(id, new EvictionCallback() {
|
|
289
|
+
@Override
|
|
290
|
+
public void onComplete(boolean memSuccess, @Nullable Exception memError) {
|
|
291
|
+
// Combine results: success only if both succeeded (or memory failed gracefully)
|
|
292
|
+
boolean combined = diskSuccess && memSuccess;
|
|
293
|
+
Exception combinedEx = diskError != null ? diskError : memError;
|
|
294
|
+
if (callback != null) {
|
|
295
|
+
callback.onComplete(combined, combinedEx);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
284
302
|
return;
|
|
285
303
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
304
|
+
|
|
305
|
+
// Check if we have an enhanced disk cache
|
|
306
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
307
|
+
synchronized (this) {
|
|
308
|
+
dc = diskCache;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (s.sourceKey != null && s.signature != null) {
|
|
312
|
+
// Use efficient model/signature-based eviction
|
|
313
|
+
// Log.i(TAG, "EvictionManager evictAllForId using efficient disk cache eviction for: " + id);
|
|
314
|
+
evictDiskByModelAndSignature(s.sourceKey, s.signature, new EvictionCallback() {
|
|
315
|
+
@Override
|
|
316
|
+
public void onComplete(boolean diskSuccess, @Nullable Exception diskError) {
|
|
317
|
+
// After disk eviction, evict from memory
|
|
318
|
+
mainHandler.post(() -> {
|
|
319
|
+
boolean memSuccess = true;
|
|
320
|
+
Exception memError = null;
|
|
321
|
+
|
|
322
|
+
ModelSignatureMemoryCache mc;
|
|
323
|
+
synchronized (EvictionManager.this) {
|
|
324
|
+
mc = memoryCache;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (mc != null && s.sourceKey != null && s.signature != null) {
|
|
328
|
+
try {
|
|
329
|
+
mc.removeByModelAndSignature(s.sourceKey, s.signature);
|
|
330
|
+
} catch (Exception e) {
|
|
331
|
+
memSuccess = false;
|
|
332
|
+
memError = e;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
boolean combined = diskSuccess && memSuccess;
|
|
337
|
+
Exception combinedEx = diskError != null ? diskError : memError;
|
|
338
|
+
|
|
339
|
+
if (callback != null) {
|
|
340
|
+
callback.onComplete(combined, combinedEx);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
298
346
|
}
|
|
299
347
|
|
|
300
348
|
/** Backwards-compatible no-callback variant */
|
|
@@ -308,61 +356,24 @@ public final class EvictionManager {
|
|
|
308
356
|
public void evictDiskForId(final String id, @Nullable final EvictionCallback callback) {
|
|
309
357
|
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
310
358
|
if (s == null) {
|
|
311
|
-
// fallback to deleting source by id-only ObjectKey
|
|
312
|
-
final Key fallbackSource = new com.bumptech.glide.signature.ObjectKey(id);
|
|
313
|
-
diskExecutor.execute(() -> {
|
|
314
|
-
boolean success = true;
|
|
315
|
-
Exception ex = null;
|
|
316
|
-
DiskCache dc;
|
|
317
|
-
synchronized (EvictionManager.this) {
|
|
318
|
-
dc = diskCache;
|
|
319
|
-
}
|
|
320
|
-
if (dc != null) {
|
|
321
|
-
try {
|
|
322
|
-
dc.delete(fallbackSource);
|
|
323
|
-
} catch (Exception e) {
|
|
324
|
-
success = false;
|
|
325
|
-
ex = e;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (callback != null) {
|
|
329
|
-
final boolean finalSuccess = success;
|
|
330
|
-
final Exception finalEx = ex;
|
|
331
|
-
mainHandler.post(() -> callback.onComplete(finalSuccess, finalEx));
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
359
|
return;
|
|
335
360
|
}
|
|
336
361
|
|
|
337
362
|
diskExecutor.execute(() -> {
|
|
338
363
|
boolean success = true;
|
|
339
364
|
Exception ex = null;
|
|
340
|
-
|
|
365
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
341
366
|
synchronized (EvictionManager.this) {
|
|
342
367
|
dc = diskCache;
|
|
343
368
|
}
|
|
344
369
|
if (dc != null) {
|
|
370
|
+
// Delete source data using DataCacheKey (sourceKey + signature)
|
|
345
371
|
try {
|
|
346
|
-
dc.
|
|
372
|
+
dc.removeByModelAndSignature(s.sourceKey, s.signature);
|
|
347
373
|
} catch (Exception e) {
|
|
348
374
|
success = false;
|
|
349
375
|
ex = e;
|
|
350
376
|
}
|
|
351
|
-
try {
|
|
352
|
-
Key resourceKey = new RecreatedResourceKey(
|
|
353
|
-
s.sourceKey,
|
|
354
|
-
s.signature,
|
|
355
|
-
s.width,
|
|
356
|
-
s.height,
|
|
357
|
-
getTransformationBytesFromStoredKeys(s),
|
|
358
|
-
s.decodedResourceClass,
|
|
359
|
-
s.optionsKeyBytes);
|
|
360
|
-
dc.delete(resourceKey);
|
|
361
|
-
} catch (Exception e) {
|
|
362
|
-
if (ex == null)
|
|
363
|
-
ex = e;
|
|
364
|
-
success = false;
|
|
365
|
-
}
|
|
366
377
|
}
|
|
367
378
|
if (callback != null) {
|
|
368
379
|
final boolean finalSuccess = success;
|
|
@@ -377,86 +388,23 @@ public final class EvictionManager {
|
|
|
377
388
|
evictDiskForId(id, null);
|
|
378
389
|
}
|
|
379
390
|
|
|
380
|
-
/** Evict only source/raw bytes (disk). */
|
|
381
|
-
public void evictSourceForId(final String id, @Nullable final EvictionCallback callback) {
|
|
382
|
-
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
383
|
-
final Key sourceKey = (s != null && s.sourceKey != null) ? s.sourceKey
|
|
384
|
-
: new com.bumptech.glide.signature.ObjectKey(id);
|
|
385
|
-
diskExecutor.execute(() -> {
|
|
386
|
-
boolean success = true;
|
|
387
|
-
Exception ex = null;
|
|
388
|
-
DiskCache dc;
|
|
389
|
-
synchronized (EvictionManager.this) {
|
|
390
|
-
dc = diskCache;
|
|
391
|
-
}
|
|
392
|
-
if (dc != null) {
|
|
393
|
-
try {
|
|
394
|
-
dc.delete(sourceKey);
|
|
395
|
-
} catch (Exception e) {
|
|
396
|
-
success = false;
|
|
397
|
-
ex = e;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (callback != null) {
|
|
401
|
-
final boolean finalSuccess = success;
|
|
402
|
-
final Exception finalEx = ex;
|
|
403
|
-
mainHandler.post(() -> callback.onComplete(finalSuccess, finalEx));
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/** Evict only the transformed resource (disk). */
|
|
409
|
-
public void evictTransformedForId(final String id, @Nullable final EvictionCallback callback) {
|
|
410
|
-
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
411
|
-
if (s == null) {
|
|
412
|
-
if (callback != null)
|
|
413
|
-
mainHandler.post(() -> callback.onComplete(false, null));
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
final byte[] transformationBytes = getTransformationBytesFromStoredKeys(s);
|
|
417
|
-
final byte[] optionsBytes = s.optionsKeyBytes;
|
|
418
|
-
|
|
419
|
-
diskExecutor.execute(() -> {
|
|
420
|
-
boolean success = true;
|
|
421
|
-
Exception ex = null;
|
|
422
|
-
DiskCache dc;
|
|
423
|
-
synchronized (EvictionManager.this) {
|
|
424
|
-
dc = diskCache;
|
|
425
|
-
}
|
|
426
|
-
if (dc != null) {
|
|
427
|
-
try {
|
|
428
|
-
Key resourceKey = new RecreatedResourceKey(s.sourceKey, s.signature, s.width, s.height, transformationBytes,
|
|
429
|
-
s.decodedResourceClass, optionsBytes);
|
|
430
|
-
dc.delete(resourceKey);
|
|
431
|
-
} catch (Exception e) {
|
|
432
|
-
success = false;
|
|
433
|
-
ex = e;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (callback != null) {
|
|
437
|
-
final boolean finalSuccess = success;
|
|
438
|
-
final Exception finalEx = ex;
|
|
439
|
-
mainHandler.post(() -> callback.onComplete(finalSuccess, finalEx));
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
|
|
444
391
|
/**
|
|
445
392
|
* Evict only from memory (must be called on main thread). Callback invoked on
|
|
446
393
|
* main thread.
|
|
447
394
|
*/
|
|
448
395
|
@MainThread
|
|
449
396
|
public void evictMemoryForId(final String id, @Nullable final EvictionCallback callback) {
|
|
450
|
-
|
|
451
|
-
final
|
|
397
|
+
// Use the merged read to get engineKey from in-memory store (not persisted)
|
|
398
|
+
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
399
|
+
// final Key engineKey = (s != null) ? s.engineKey : null;
|
|
452
400
|
boolean success = true;
|
|
453
401
|
Exception ex = null;
|
|
454
|
-
if (
|
|
402
|
+
if (s == null) {
|
|
455
403
|
if (callback != null)
|
|
456
404
|
callback.onComplete(false, null);
|
|
457
405
|
return;
|
|
458
406
|
}
|
|
459
|
-
|
|
407
|
+
ModelSignatureMemoryCache mc;
|
|
460
408
|
synchronized (this) {
|
|
461
409
|
mc = memoryCache;
|
|
462
410
|
}
|
|
@@ -466,7 +414,8 @@ public final class EvictionManager {
|
|
|
466
414
|
return;
|
|
467
415
|
}
|
|
468
416
|
try {
|
|
469
|
-
mc.
|
|
417
|
+
mc.removeByModelAndSignature(s.sourceKey, s.signature );
|
|
418
|
+
clearAllActiveResources();
|
|
470
419
|
} catch (Exception e) {
|
|
471
420
|
success = false;
|
|
472
421
|
ex = e;
|
|
@@ -494,18 +443,17 @@ public final class EvictionManager {
|
|
|
494
443
|
*/
|
|
495
444
|
@MainThread
|
|
496
445
|
public boolean isInMemoryCache(final String id) {
|
|
497
|
-
final CacheKeyStore.StoredKeys s = (
|
|
498
|
-
if (s == null || s.
|
|
446
|
+
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
447
|
+
if (s == null || s.sourceKey == null || s.signature == null)
|
|
499
448
|
return false;
|
|
500
|
-
|
|
501
|
-
LruResourceCache mc;
|
|
449
|
+
ModelSignatureMemoryCache mc;
|
|
502
450
|
synchronized (this) {
|
|
503
451
|
mc = memoryCache;
|
|
504
452
|
}
|
|
505
453
|
if (mc == null)
|
|
506
454
|
return false;
|
|
507
455
|
|
|
508
|
-
return mc.
|
|
456
|
+
return mc.containsByModelAndSignature(s.sourceKey, s.signature);
|
|
509
457
|
}
|
|
510
458
|
|
|
511
459
|
/**
|
|
@@ -518,36 +466,32 @@ public final class EvictionManager {
|
|
|
518
466
|
boolean sourcePresent = false;
|
|
519
467
|
boolean transformedPresent = false;
|
|
520
468
|
|
|
521
|
-
final CacheKeyStore.StoredKeys s = (
|
|
522
|
-
|
|
523
|
-
DiskCache dc;
|
|
469
|
+
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
470
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
524
471
|
synchronized (EvictionManager.this) {
|
|
525
472
|
dc = diskCache;
|
|
526
473
|
}
|
|
527
474
|
|
|
528
475
|
if (dc == null) {
|
|
529
|
-
mainHandler.post(() -> callback.onResult(false
|
|
476
|
+
mainHandler.post(() -> callback.onResult(false));
|
|
530
477
|
return;
|
|
531
478
|
}
|
|
532
479
|
|
|
533
480
|
try {
|
|
534
|
-
if (s
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
Key resourceKey = new RecreatedResourceKey(s.sourceKey, s.signature, s.width, s.height, transformationBytes,
|
|
538
|
-
s.decodedResourceClass, s.optionsKeyBytes);
|
|
539
|
-
transformedPresent = dc.get(resourceKey) != null;
|
|
481
|
+
if (hasValidStoredKeys(s)) {
|
|
482
|
+
// Log.d(TAG, "EvictionManager isInDiskCacheAsync " + id + " " + s.sourceKey+ " " + s.sourceKey.getClass().getName()+ " " + s.signature);
|
|
483
|
+
sourcePresent = dc.containsByModelAndSignature(s.sourceKey, s.signature);
|
|
540
484
|
} else {
|
|
541
485
|
// fallback: check ObjectKey(id) as source
|
|
542
486
|
Key fallback = new com.bumptech.glide.signature.ObjectKey(id);
|
|
543
487
|
sourcePresent = dc.get(fallback) != null;
|
|
488
|
+
// Log.d(TAG, "EvictionManager fallback check with ObjectKey: " + sourcePresent);
|
|
544
489
|
}
|
|
545
|
-
} catch (Exception
|
|
490
|
+
} catch (Exception e) {
|
|
491
|
+
Log.e(TAG, "EvictionManager isInDiskCacheAsync exception for " + id, e);
|
|
546
492
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
final boolean finalTransformed = transformedPresent;
|
|
550
|
-
mainHandler.post(() -> callback.onResult(finalSource, finalTransformed));
|
|
493
|
+
final Boolean result = sourcePresent;
|
|
494
|
+
mainHandler.post(() -> callback.onResult(result));
|
|
551
495
|
});
|
|
552
496
|
}
|
|
553
497
|
|
|
@@ -556,180 +500,234 @@ public final class EvictionManager {
|
|
|
556
500
|
* only.
|
|
557
501
|
* Returns array [sourcePresent, transformedPresent].
|
|
558
502
|
*/
|
|
559
|
-
public boolean
|
|
560
|
-
final CacheKeyStore.StoredKeys s = (
|
|
561
|
-
|
|
503
|
+
public boolean isInDiskCacheBlocking(final String id) {
|
|
504
|
+
final CacheKeyStore.StoredKeys s = readStoredKeysPreferPersistent(id);
|
|
505
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
562
506
|
synchronized (EvictionManager.this) {
|
|
563
507
|
dc = diskCache;
|
|
564
508
|
}
|
|
565
509
|
boolean sourcePresent = false;
|
|
566
510
|
boolean transformedPresent = false;
|
|
567
511
|
if (dc == null)
|
|
568
|
-
return
|
|
512
|
+
return false;
|
|
569
513
|
try {
|
|
570
|
-
if (s
|
|
571
|
-
|
|
572
|
-
Key resourceKey = new RecreatedResourceKey(s.sourceKey, s.signature, s.width, s.height,
|
|
573
|
-
getTransformationBytesFromStoredKeys(s), s.decodedResourceClass, s.optionsKeyBytes);
|
|
574
|
-
transformedPresent = dc.get(resourceKey) != null;
|
|
514
|
+
if (hasValidStoredKeys(s)) {
|
|
515
|
+
sourcePresent = dc.containsByModelAndSignature(s.sourceKey, s.signature);
|
|
575
516
|
} else {
|
|
576
517
|
Key fallback = new com.bumptech.glide.signature.ObjectKey(id);
|
|
577
518
|
sourcePresent = dc.get(fallback) != null;
|
|
578
519
|
}
|
|
579
520
|
} catch (Exception ignored) {
|
|
580
521
|
}
|
|
581
|
-
return
|
|
522
|
+
return sourcePresent;
|
|
582
523
|
}
|
|
583
524
|
|
|
525
|
+
@Nullable
|
|
526
|
+
public CacheKeyStore.StoredKeys readStoredKeysPreferPersistent(String id) {
|
|
527
|
+
CacheKeyStore.StoredKeys s = null;
|
|
528
|
+
|
|
529
|
+
// First check in-memory
|
|
530
|
+
CacheKeyStore.StoredKeys inMem = inMemoryKeyStore.get(id);
|
|
531
|
+
CacheKeyStore.StoredKeys persistent = null;
|
|
532
|
+
|
|
533
|
+
if (persistentStore != null) {
|
|
534
|
+
persistent = persistentStore.get(id);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Return whichever we have (prefer persistent)
|
|
538
|
+
return persistent != null ? persistent : inMem;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Helper to check if StoredKeys has all required components for disk cache operations.
|
|
543
|
+
* @param s the StoredKeys to check
|
|
544
|
+
* @return true if s has non-null sourceKey and signature
|
|
545
|
+
*/
|
|
546
|
+
private static boolean hasValidStoredKeys(@Nullable CacheKeyStore.StoredKeys s) {
|
|
547
|
+
return s != null && s.sourceKey != null && s.signature != null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
// -----------------------
|
|
552
|
+
// Enhanced disk cache eviction methods using ModelSignatureDiskLruCacheWrapper
|
|
553
|
+
// -----------------------
|
|
554
|
+
|
|
584
555
|
/**
|
|
585
|
-
*
|
|
556
|
+
* Evict all disk cache entries (both source and transformed) matching model and signature
|
|
557
|
+
*
|
|
558
|
+
* @param model The model (e.g., URI)
|
|
559
|
+
* @param signature The signature Key
|
|
560
|
+
* @param callback Optional callback invoked on main thread
|
|
586
561
|
*/
|
|
587
|
-
|
|
588
|
-
final
|
|
589
|
-
final Key signature,
|
|
590
|
-
final int width,
|
|
591
|
-
final int height,
|
|
592
|
-
final byte[] transformationKeyBytes,
|
|
593
|
-
final Class<?> decodedResourceClass,
|
|
594
|
-
final byte[] optionsKeyBytes,
|
|
595
|
-
final Key engineKey,
|
|
562
|
+
public void evictDiskByModelAndSignature(
|
|
563
|
+
@Nullable final Object model,
|
|
564
|
+
@Nullable final Key signature,
|
|
596
565
|
@Nullable final EvictionCallback callback) {
|
|
566
|
+
|
|
567
|
+
if (model == null || signature == null) {
|
|
568
|
+
if (callback != null) {
|
|
569
|
+
mainHandler.post(() -> callback.onComplete(false, new IllegalArgumentException("model and signature must not be null")));
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
597
573
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
574
|
+
diskExecutor.execute(() -> {
|
|
575
|
+
boolean success = false;
|
|
576
|
+
Exception ex = null;
|
|
577
|
+
int removed = 0;
|
|
578
|
+
|
|
579
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
580
|
+
synchronized (EvictionManager.this) {
|
|
581
|
+
dc = diskCache;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
removed = dc.removeByModelAndSignature(model, signature);
|
|
585
|
+
success = true;
|
|
586
|
+
|
|
587
|
+
if (callback != null) {
|
|
588
|
+
final boolean finalSuccess = success;
|
|
589
|
+
final Exception finalEx = ex;
|
|
590
|
+
mainHandler.post(() -> callback.onComplete(finalSuccess, finalEx));
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Remove all disk cache entries that do NOT match the given signature.
|
|
597
|
+
* Useful for cache invalidation when changing signature versions.
|
|
598
|
+
*
|
|
599
|
+
* @param currentSignature The current/valid signature to keep
|
|
600
|
+
* @param callback Optional callback invoked on main thread
|
|
601
|
+
*/
|
|
602
|
+
public void evictDiskExceptSignature(
|
|
603
|
+
@Nullable final Key currentSignature,
|
|
604
|
+
@Nullable final EvictionCallback callback) {
|
|
605
|
+
|
|
606
|
+
if (currentSignature == null) {
|
|
607
|
+
if (callback != null) {
|
|
608
|
+
mainHandler.post(() -> callback.onComplete(false, new IllegalArgumentException("currentSignature must not be null")));
|
|
623
609
|
}
|
|
624
610
|
return;
|
|
625
611
|
}
|
|
626
612
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
613
|
+
diskExecutor.execute(() -> {
|
|
614
|
+
boolean success = false;
|
|
615
|
+
Exception ex = null;
|
|
616
|
+
int removed = 0;
|
|
617
|
+
|
|
618
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
619
|
+
synchronized (EvictionManager.this) {
|
|
620
|
+
dc = diskCache;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
removed = dc.removeAllExceptSignature(currentSignature);
|
|
624
|
+
success = true;
|
|
625
|
+
|
|
626
|
+
if (callback != null) {
|
|
627
|
+
final boolean finalSuccess = success;
|
|
628
|
+
final Exception finalEx = ex;
|
|
629
|
+
mainHandler.post(() -> callback.onComplete(finalSuccess, finalEx));
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
630
633
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
dc.delete(sourceKey);
|
|
641
|
-
} catch (Exception e) {
|
|
642
|
-
failed.set(true);
|
|
643
|
-
firstException.compareAndSet(null, e);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
if (remaining.decrementAndGet() == 0) {
|
|
647
|
-
mainHandler.post(() -> {
|
|
648
|
-
if (engineKey != null) {
|
|
649
|
-
MemoryCache mc;
|
|
650
|
-
synchronized (EvictionManager.this) {
|
|
651
|
-
mc = memoryCache;
|
|
652
|
-
}
|
|
653
|
-
if (mc != null) {
|
|
654
|
-
try {
|
|
655
|
-
mc.remove(engineKey);
|
|
656
|
-
} catch (Exception ignored) {
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (callback != null)
|
|
661
|
-
callback.onComplete(!failed.get(), firstException.get());
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
});
|
|
634
|
+
/**
|
|
635
|
+
* Get disk cache statistics.
|
|
636
|
+
*
|
|
637
|
+
* @return Array: [indexSize, diskCacheSize] or null if not supported
|
|
638
|
+
*/
|
|
639
|
+
public long[] getDiskCacheStats() {
|
|
640
|
+
ModelSignatureDiskLruCacheWrapper dc;
|
|
641
|
+
synchronized (this) {
|
|
642
|
+
dc = diskCache;
|
|
665
643
|
}
|
|
644
|
+
|
|
645
|
+
return dc.getStats();
|
|
646
|
+
}
|
|
666
647
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
648
|
+
/**
|
|
649
|
+
* Initialize reflection fields/methods for ActiveResources access (once only)
|
|
650
|
+
*/
|
|
651
|
+
private static void initActiveResourcesReflection() {
|
|
652
|
+
if (reflectionInitialized || reflectionFailed) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
synchronized (EvictionManager.class) {
|
|
656
|
+
if (reflectionInitialized || reflectionFailed) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
// Get Glide.engine field
|
|
661
|
+
engineField = com.bumptech.glide.Glide.class.getDeclaredField("engine");
|
|
662
|
+
engineField.setAccessible(true);
|
|
663
|
+
|
|
664
|
+
// Get Engine.activeResources field
|
|
665
|
+
Class<?> engineClass = Class.forName("com.bumptech.glide.load.engine.Engine");
|
|
666
|
+
activeResourcesField = engineClass.getDeclaredField("activeResources");
|
|
667
|
+
activeResourcesField.setAccessible(true);
|
|
668
|
+
|
|
669
|
+
Class<?> activeResourcesClass = Class.forName("com.bumptech.glide.load.engine.ActiveResources");
|
|
670
|
+
|
|
671
|
+
// Get ActiveResources.activeEngineResources field (the actual map)
|
|
672
|
+
try {
|
|
673
|
+
activeEngineResourcesField = activeResourcesClass.getDeclaredField("activeEngineResources");
|
|
674
|
+
activeEngineResourcesField.setAccessible(true);
|
|
675
|
+
} catch (NoSuchFieldException e) {
|
|
676
|
+
// Try alternative field names in different Glide versions
|
|
675
677
|
try {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
failed.set(true);
|
|
681
|
-
firstException.compareAndSet(null, e);
|
|
678
|
+
activeEngineResourcesField = activeResourcesClass.getDeclaredField("activeResources");
|
|
679
|
+
activeEngineResourcesField.setAccessible(true);
|
|
680
|
+
} catch (NoSuchFieldException e2) {
|
|
681
|
+
Log.w(TAG, "Could not find activeEngineResources map field");
|
|
682
682
|
}
|
|
683
683
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
if (mc != null) {
|
|
692
|
-
try {
|
|
693
|
-
mc.remove(engineKey);
|
|
694
|
-
} catch (Exception ignored) {
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
if (callback != null)
|
|
699
|
-
callback.onComplete(!failed.get(), firstException.get());
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
@Nullable
|
|
707
|
-
private CacheKeyStore.StoredKeys readStoredKeysPreferPersistent(String id) {
|
|
708
|
-
if (persistentStore != null) {
|
|
709
|
-
CacheKeyStore.StoredKeys s = persistentStore.get(id);
|
|
710
|
-
if (s != null) {
|
|
711
|
-
// keep mirrored in-memory copy for engineKey augmentation
|
|
712
|
-
inMemoryKeyStore.put(id, s);
|
|
713
|
-
return s;
|
|
684
|
+
|
|
685
|
+
reflectionInitialized = true;
|
|
686
|
+
// Log.i(TAG, "ActiveResources reflection initialized successfully");
|
|
687
|
+
} catch (Throwable t) {
|
|
688
|
+
reflectionFailed = true;
|
|
689
|
+
Log.w(TAG, "Failed to initialize ActiveResources reflection (may not exist in this Glide version)", t);
|
|
714
690
|
}
|
|
715
691
|
}
|
|
716
|
-
return inMemoryKeyStore.get(id);
|
|
717
692
|
}
|
|
718
693
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
694
|
+
/**
|
|
695
|
+
* Clear all entries from Glide's ActiveResources cache.
|
|
696
|
+
* Must be called on main thread.
|
|
697
|
+
*
|
|
698
|
+
* @return true if successfully cleared, false if reflection failed
|
|
699
|
+
*/
|
|
700
|
+
@MainThread
|
|
701
|
+
public boolean clearAllActiveResources() {
|
|
702
|
+
try {
|
|
703
|
+
initActiveResourcesReflection();
|
|
704
|
+
|
|
705
|
+
if (reflectionFailed) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
Object engine = engineField.get(glideInstance);
|
|
710
|
+
|
|
711
|
+
if (engine != null) {
|
|
712
|
+
Object activeResources = activeResourcesField.get(engine);
|
|
713
|
+
|
|
714
|
+
if (activeResources != null && activeEngineResourcesField != null) {
|
|
715
|
+
// Get the map and clear it
|
|
716
|
+
Object mapObject = activeEngineResourcesField.get(activeResources);
|
|
717
|
+
|
|
718
|
+
if (mapObject instanceof java.util.Map) {
|
|
719
|
+
java.util.Map<?, ?> map = (java.util.Map<?, ?>) mapObject;
|
|
720
|
+
int size = map.size();
|
|
721
|
+
map.clear();
|
|
722
|
+
// Log.i(TAG, "Cleared " + size + " entries from ActiveResources map");
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
731
726
|
}
|
|
727
|
+
} catch (Throwable t) {
|
|
728
|
+
Log.w(TAG, "Failed to clear ActiveResources map", t);
|
|
732
729
|
}
|
|
733
|
-
|
|
730
|
+
|
|
731
|
+
return false;
|
|
734
732
|
}
|
|
735
733
|
}
|