Enabling the host application to control the timing of attaching the |FlutterView| to the engine (flutter/engine#43595)

In the add-to-app scenario where multiple FlutterViews share the same FlutterEngine, the host application desires to determine the timing of attaching the FlutterView to the engine, for example, during the `onResume` instead of the `onCreateView`.

As an example, consider the following scenario: A native page contains multiple tabs, and each tab is a FlutterFragment. During initialization, FlutterFragments of different tabs are created almost simultaneously, but only the one that needs to be displayed currently requires attachment to the engine, while the others need to be attached only when they receive the |onResume| callback.

Partial fix: https://github.com/flutter/flutter/issues/130235

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Rulong Chen(陈汝龙)
2023-08-16 10:34:04 +08:00
committed by GitHub
parent 3df865c3fe
commit 75ac0d3957
5 changed files with 75 additions and 2 deletions

View File

@@ -1407,6 +1407,19 @@ public class FlutterActivity extends Activity
return true;
}
/**
* Whether to automatically attach the {@link FlutterView} to the engine.
*
* <p>Returning {@code false} means that the task of attaching the {@link FlutterView} to the
* engine will be taken over by the host application.
*
* <p>Defaults to {@code true}.
*/
@Override
public boolean attachToEngineAutomatically() {
return true;
}
@Override
public boolean popSystemNavigator() {
// Hook for subclass. No-op if returns false.

View File

@@ -390,8 +390,10 @@ import java.util.List;
// Add listener to be notified when Flutter renders its first frame.
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
flutterView.attachToFlutterEngine(flutterEngine);
if (host.attachToEngineAutomatically()) {
Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
flutterView.attachToFlutterEngine(flutterEngine);
}
flutterView.setId(flutterViewId);
if (shouldDelayFirstAndroidViewDraw) {
@@ -1171,5 +1173,17 @@ import java.util.List;
* while return {@code true} means the engine dispatches these events.
*/
boolean shouldDispatchAppLifecycleState();
/**
* Whether to automatically attach the {@link FlutterView} to the engine.
*
* <p>In the add-to-app scenario where multiple {@link FlutterView} share the same {@link
* FlutterEngine}, the host application desires to determine the timing of attaching the {@link
* FlutterView} to the engine, for example, during the {@code onResume} instead of the {@code
* onCreateView}.
*
* <p>Defaults to {@code true}.
*/
boolean attachToEngineAutomatically();
}
}

View File

@@ -1645,6 +1645,19 @@ public class FlutterFragment extends Fragment
return true;
}
/**
* Whether to automatically attach the {@link FlutterView} to the engine.
*
* <p>Returning {@code false} means that the task of attaching the {@link FlutterView} to the
* engine will be taken over by the host application.
*
* <p>Defaults to {@code true}.
*/
@Override
public boolean attachToEngineAutomatically() {
return true;
}
/**
* {@inheritDoc}
*

View File

@@ -2,9 +2,11 @@ package io.flutter.embedding.android;
import static android.content.ComponentCallbacks2.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull;
@@ -88,6 +90,7 @@ public class FlutterActivityAndFragmentDelegateTest {
when(mockHost.shouldHandleDeeplinking()).thenReturn(false);
when(mockHost.shouldDestroyEngineWithHost()).thenReturn(true);
when(mockHost.shouldDispatchAppLifecycleState()).thenReturn(true);
when(mockHost.attachToEngineAutomatically()).thenReturn(true);
}
@Test
@@ -1241,6 +1244,31 @@ public class FlutterActivityAndFragmentDelegateTest {
assertEquals(engineUnderTest, mockFlutterEngine);
}
@Test
public void itDoesAttachFlutterViewToEngine() {
// ---- Test setup ----
// Create the real object that we're testing.
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
delegate.onAttach(ctx);
delegate.onCreateView(null, null, null, 0, true);
// --- Execute the behavior under test ---
assertTrue(delegate.flutterView.isAttachedToFlutterEngine());
}
@Test
public void itDoesNotAttachFlutterViewToEngine() {
// ---- Test setup ----
// Create the real object that we're testing.
when(mockHost.attachToEngineAutomatically()).thenReturn(false);
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
delegate.onAttach(ctx);
delegate.onCreateView(null, null, null, 0, true);
// --- Execute the behavior under test ---
assertFalse(delegate.flutterView.isAttachedToFlutterEngine());
}
/**
* Creates a mock {@link io.flutter.embedding.engine.FlutterEngine}.
*

View File

@@ -389,6 +389,11 @@ public class FlutterAndroidComponentTest {
return true;
}
@Override
public boolean attachToEngineAutomatically() {
return true;
}
@Override
public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {}