-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Describe the issue
I identified a resource retention / memory leak issue in the Android implementation of the plugin (com.squareup.sdk.reader.flutter.internal.DateFormatUtils).
The class uses a private static final ThreadLocal to cache SimpleDateFormat instances. However, there is no mechanism to invoke remove() on this ThreadLocal.
On Android, where threads (e.g., from the standard thread pool or main looper) often outlive the specific Flutter Engine or Activity lifecycle, holding these instances statically prevents the ClassLoader (and potentially associated resources) from being garbage collected. This effectively creates a memory leak that accumulates every time the class is loaded in a new context (e.g. Flutter engine restart).
Source Code Location:
// android/src/main/java/com/squareup/sdk/reader/flutter/internal/DateFormatUtils.java
private static final ThreadLocal ISO_8601 = new ThreadLocal() {
@OverRide protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
}
};
// .remove() is never called.
To Reproduce
-
Initialize the Square Reader SDK in a Flutter Android app.
-
Trigger functionality that invokes DateFormatUtils.formatISO8601UTC (populating the ThreadLocalMap of the current thread).
-
Detach/Destroy the Flutter Engine (or recreate the Activity/Context multiple times).
-
Observation: Inspect the Heap Dump. You will see SimpleDateFormat instances referenced by ThreadLocalMap entries of alive threads, preventing proper GC of the plugin's classes.
Expected behavior
The utility class should avoid using static ThreadLocal for SimpleDateFormat. On modern Android (ART), the cost of creating a new SimpleDateFormat is negligible compared to the risk of memory leaks. The cache should be removed, or a cleanup mechanism must be implemented.
Suggested Fix
Replace the ThreadLocal cache with a local variable instantiation.
// Recommended change in DateFormatUtils.java
public static String formatISO8601UTC(Date date) {
// Thread-safe, no leak risk
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date);
}