Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.25+1

* Fixes crash on hot restart caused by stale observers.

## 0.6.25

* Adds support for `MediaSettings.fps` for camera preview, image streaming, and video recording.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ public void onChanged(T t) {
new ProxyApiRegistrar.FlutterMethodRunnable() {
@Override
public void run() {
// Check if this observer instance is still in the instance manager.
// During hot restart, old observers may remain attached to LiveData but
// are no longer tracked in the instance manager.
if (!api.getPigeonRegistrar()
.getInstanceManager()
.containsInstance(ObserverImpl.this)) {
android.util.Log.w(
"ObserverProxyApi",
"Ignoring onChanged callback for Observer not in InstanceManager (likely from previous hot restart): "
+ ObserverImpl.this);
return;
}

api.onChanged(
ObserverImpl.this,
t,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,43 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class ObserverTest {
private ObserverProxyApi mockApi;
private TestProxyApiRegistrar registrar;
private ObserverProxyApi.ObserverImpl<String> instance;

@Before
public void setUp() {
mockApi = mock(ObserverProxyApi.class);
registrar = new TestProxyApiRegistrar();
when(mockApi.getPigeonRegistrar()).thenReturn(registrar);
instance = new ObserverProxyApi.ObserverImpl<>(mockApi);
}

@Test
public void onChanged_makesExpectedCallToDartCallback() {
final ObserverProxyApi mockApi = mock(ObserverProxyApi.class);
when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar());
// Add the observer to the instance manager to simulate normal operation
registrar.getInstanceManager().addDartCreatedInstance(instance, 0);

final ObserverProxyApi.ObserverImpl<String> instance =
new ObserverProxyApi.ObserverImpl<>(mockApi);
final String value = "result";
instance.onChanged(value);

verify(mockApi).onChanged(eq(instance), eq(value), any());
}

@Test
public void onChanged_doesNotCallDartCallbackWhenObserverNotInInstanceManager() {
final String value = "result";
instance.onChanged(value);

// Verify that the Dart callback is NOT invoked for stale observers
verify(mockApi, never()).onChanged(any(), any(), any());
}
}
2 changes: 1 addition & 1 deletion packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android_camerax
description: Android implementation of the camera plugin using the CameraX library.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.6.25
version: 0.6.25+1

environment:
sdk: ^3.9.0
Expand Down