Skip to content

Commit

Permalink
fix(storage, android): stream handler & event channel clean up on com…
Browse files Browse the repository at this point in the history
…pletion (#13508)

* fix(storage, android): stream handler and event channel was never cleaned up on completion causing out-of-memory exception

* chore: format
  • Loading branch information
russellwheatley authored Oct 18, 2024
1 parent 0158ad2 commit f010b46
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public class FlutterFirebaseStoragePlugin
static final String STORAGE_TASK_EVENT_NAME = "taskEvent";
static final String DEFAULT_ERROR_CODE = "firebase_storage";

private final Map<String, EventChannel> eventChannels = new HashMap<>();
private final Map<String, StreamHandler> streamHandlers = new HashMap<>();
static final Map<String, EventChannel> eventChannels = new HashMap<>();
static final Map<String, StreamHandler> streamHandlers = new HashMap<>();

static Map<String, String> getExceptionDetails(Exception exception) {
Map<String, String> details = new HashMap<>();
Expand Down Expand Up @@ -145,11 +145,6 @@ private void initInstance(BinaryMessenger messenger) {
this.messenger = messenger;
}

private String registerEventChannel(String prefix, StreamHandler handler) {
String identifier = UUID.randomUUID().toString().toLowerCase(Locale.US);
return registerEventChannel(prefix, identifier, handler);
}

private String registerEventChannel(String prefix, String identifier, StreamHandler handler) {
final String channelName = prefix + "/" + identifier;

Expand Down Expand Up @@ -459,10 +454,12 @@ public void referencePutData(
FlutterFirebaseStorageTask.uploadBytes(
handle.intValue(), androidReference, data, androidMetaData);
try {
TaskStateChannelStreamHandler handler = storageTask.startTaskWithMethodChannel(channel);
String identifier = UUID.randomUUID().toString().toLowerCase(Locale.US);
TaskStateChannelStreamHandler handler =
storageTask.startTaskWithMethodChannel(channel, identifier);
result.success(
registerEventChannel(
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, handler));
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, identifier, handler));
} catch (Exception e) {
result.error(FlutterFirebaseStorageException.parserExceptionToFlutter(e));
}
Expand All @@ -489,10 +486,12 @@ public void referencePutString(
androidMetaData);

try {
TaskStateChannelStreamHandler handler = storageTask.startTaskWithMethodChannel(channel);
String identifier = UUID.randomUUID().toString().toLowerCase(Locale.US);
TaskStateChannelStreamHandler handler =
storageTask.startTaskWithMethodChannel(channel, identifier);
result.success(
registerEventChannel(
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, handler));
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, identifier, handler));
} catch (Exception e) {
result.error(FlutterFirebaseStorageException.parserExceptionToFlutter(e));
}
Expand All @@ -517,10 +516,12 @@ public void referencePutFile(
settableMetaData == null ? null : getMetaDataFromPigeon(settableMetaData));

try {
TaskStateChannelStreamHandler handler = storageTask.startTaskWithMethodChannel(channel);
String identifier = UUID.randomUUID().toString().toLowerCase(Locale.US);
TaskStateChannelStreamHandler handler =
storageTask.startTaskWithMethodChannel(channel, identifier);
result.success(
registerEventChannel(
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, handler));
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, identifier, handler));
} catch (Exception e) {
result.error(FlutterFirebaseStorageException.parserExceptionToFlutter(e));
}
Expand All @@ -540,10 +541,12 @@ public void referenceDownloadFile(
handle.intValue(), androidReference, new File(filePath));

try {
TaskStateChannelStreamHandler handler = storageTask.startTaskWithMethodChannel(channel);
String identifier = UUID.randomUUID().toString().toLowerCase(Locale.US);
TaskStateChannelStreamHandler handler =
storageTask.startTaskWithMethodChannel(channel, identifier);
result.success(
registerEventChannel(
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, handler));
STORAGE_METHOD_CHANNEL_NAME + "/" + STORAGE_TASK_EVENT_NAME, identifier, handler));
} catch (Exception e) {
result.error(FlutterFirebaseStorageException.parserExceptionToFlutter(e));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ void destroy() {
}
}

TaskStateChannelStreamHandler startTaskWithMethodChannel(@NonNull MethodChannel channel)
throws Exception {
TaskStateChannelStreamHandler startTaskWithMethodChannel(
@NonNull MethodChannel channel, @NonNull String identifier) throws Exception {
if (type == FlutterFirebaseStorageTaskType.BYTES && bytes != null) {
if (metadata == null) {
storageTask = reference.putBytes(bytes);
Expand All @@ -175,7 +175,7 @@ TaskStateChannelStreamHandler startTaskWithMethodChannel(@NonNull MethodChannel
throw new Exception("Unable to start task. Some arguments have no been initialized.");
}

return new TaskStateChannelStreamHandler(this, reference.getStorage(), storageTask);
return new TaskStateChannelStreamHandler(this, reference.getStorage(), storageTask, identifier);
}

public Object getSnapshot() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageException;
import com.google.firebase.storage.StorageTask;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.EventChannel.EventSink;
import io.flutter.plugin.common.EventChannel.StreamHandler;
import java.util.HashMap;
Expand All @@ -18,6 +19,7 @@ public class TaskStateChannelStreamHandler implements StreamHandler {
private final FlutterFirebaseStorageTask flutterTask;
private final FirebaseStorage androidStorage;
private final StorageTask<?> androidTask;
private final String identifier;

private final String TASK_STATE_NAME = "taskState";
private final String TASK_APP_NAME = "appName";
Expand All @@ -27,10 +29,12 @@ public class TaskStateChannelStreamHandler implements StreamHandler {
public TaskStateChannelStreamHandler(
FlutterFirebaseStorageTask flutterTask,
FirebaseStorage androidStorage,
StorageTask androidTask) {
StorageTask androidTask,
String identifier) {
this.flutterTask = flutterTask;
this.androidStorage = androidStorage;
this.androidTask = androidTask;
this.identifier = identifier;
}

@Override
Expand Down Expand Up @@ -104,6 +108,17 @@ public void onListen(Object arguments, EventSink events) {
public void onCancel(Object arguments) {
if (!androidTask.isCanceled()) androidTask.cancel();
if (!flutterTask.isDestroyed()) flutterTask.destroy();
EventChannel eventChannel = FlutterFirebaseStoragePlugin.eventChannels.get(identifier);

// Remove stream handler and clear the event channel
if (eventChannel != null) {
eventChannel.setStreamHandler(null);
FlutterFirebaseStoragePlugin.eventChannels.remove(identifier);
}

if (FlutterFirebaseStoragePlugin.streamHandlers.get(identifier) != null) {
FlutterFirebaseStoragePlugin.streamHandlers.remove(identifier);
}
}

private Map<String, Object> getTaskEventMap(
Expand Down

0 comments on commit f010b46

Please sign in to comment.