diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 1412d9daaa8..f26109d7dcf 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -269,9 +269,18 @@ public class FlutterView extends FrameLayout { @Override protected void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - Log.v(TAG, "Configuration changed. Sending locales and user settings to Flutter."); - sendLocalesToFlutter(newConfig); - sendUserSettingsToFlutter(); + // We've observed on Android Q that going to the background, changing + // orientation, and bringing the app back to foreground results in a sequence + // of detatch from flutterEngine, onConfigurationChanged, followed by attach + // to flutterEngine. + // No-op here so that we avoid NPE; these channels will get notified once + // the activity or fragment tell the view to attach to the Flutter engine + // again (e.g. in onStart). + if (flutterEngine != null) { + Log.v(TAG, "Configuration changed. Sending locales and user settings to Flutter."); + sendLocalesToFlutter(newConfig); + sendUserSettingsToFlutter(); + } } /** diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 239045b07d4..6d10e8e9de6 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -78,6 +78,25 @@ public class FlutterViewTest { verify(platformViewsController, times(1)).detachFromView(); } + @Test + public void onConfigurationChanged_fizzlesWhenNullEngine() { + FlutterView flutterView = new FlutterView(RuntimeEnvironment.application); + FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni)); + + Configuration configuration = RuntimeEnvironment.application.getResources().getConfiguration(); + // 1 invocation of channels. + flutterView.attachToFlutterEngine(flutterEngine); + // 2 invocations of channels. + flutterView.onConfigurationChanged(configuration); + flutterView.detachFromFlutterEngine(); + + // Should fizzle. + flutterView.onConfigurationChanged(configuration); + + verify(flutterEngine, times(2)).getLocalizationChannel(); + verify(flutterEngine, times(2)).getSettingsChannel(); + } + // TODO(mattcarroll): turn this into an e2e test. GitHub #42990 @Test public void itSendsLightPlatformBrightnessToFlutter() {