未验证 提交 ccbc2e6c 编写于 作者: A Ari Weiland 提交者: GitHub

Fix some serious lifecycle bugs with Android embedding code (#22203)

上级 fcb64c98
......@@ -405,11 +405,11 @@ public class FlutterActivity extends Activity
super.onCreate(savedInstanceState);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
delegate.onActivityCreated(savedInstanceState);
delegate.onRestoreInstanceState(savedInstanceState);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
configureWindowForTransparency();
setContentView(createFlutterView());
......
......@@ -307,8 +307,10 @@ import java.util.Arrays;
return flutterSplashView;
}
void onActivityCreated(@Nullable Bundle bundle) {
Log.v(TAG, "onActivityCreated. Giving framework and plugins an opportunity to restore state.");
void onRestoreInstanceState(@Nullable Bundle bundle) {
Log.v(
TAG,
"onRestoreInstanceState. Giving framework and plugins an opportunity to restore state.");
ensureAlive();
Bundle pluginState = null;
......@@ -900,10 +902,11 @@ import java.util.Arrays;
/**
* Whether state restoration is enabled.
*
* <p>When this returns true, the instance state provided to {@code onActivityCreated(Bundle)}
* will be forwarded to the framework via the {@code RestorationChannel} and during {@code
* onSaveInstanceState(Bundle)} the current framework instance state obtained from {@code
* RestorationChannel} will be stored in the provided bundle.
* <p>When this returns true, the instance state provided to {@code
* onRestoreInstanceState(Bundle)} will be forwarded to the framework via the {@code
* RestorationChannel} and during {@code onSaveInstanceState(Bundle)} the current framework
* instance state obtained from {@code RestorationChannel} will be stored in the provided
* bundle.
*
* <p>This defaults to true, unless a cached engine is used.
*/
......
......@@ -608,6 +608,12 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
delegate.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
delegate.onRestoreInstanceState(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(
......@@ -615,12 +621,6 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
return delegate.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
delegate.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
......
......@@ -810,8 +810,8 @@ import java.util.Set;
/**
* Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its
* associated {@link Activity} has its {@code onCreate(Bundle)} method invoked, or its
* associated {@code Fragment} has its {@code onActivityCreated(Bundle)} method invoked.
* associated {@link Activity} or {@code Fragment} has its {@code onCreate(Bundle)} method
* invoked.
*/
void onRestoreInstanceState(@Nullable Bundle bundle) {
for (OnSaveInstanceStateListener listener : onSaveInstanceStateListeners) {
......
......@@ -142,8 +142,7 @@ public interface ActivityControlSurface {
/**
* Call this method from the {@link Activity} or {@code Fragment} that is attached to this {@code
* ActivityControlSurface}'s {@link FlutterEngine} when {@link Activity#onCreate(Bundle)} or
* {@code Fragment#onActivityCreated(Bundle)} is invoked in the {@link Activity} or {@code
* Fragment}.
* {@code Fragment#onCreate(Bundle)} is invoked in the {@link Activity} or {@code Fragment}.
*/
void onRestoreInstanceState(@Nullable Bundle bundle);
}
......@@ -112,7 +112,7 @@ public interface ActivityPluginBinding {
/**
* Invoked when the associated {@code Activity} executes {@link Activity#onCreate(Bundle)} or
* associated {@code Fragment} executes {@code Fragment#onActivityCreated(Bundle)}.
* associated {@code Fragment} executes {@code Fragment#onCreate(Bundle)}.
*/
void onRestoreInstanceState(@Nullable Bundle bundle);
}
......
......@@ -19,11 +19,17 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding.OnSaveInstanceStateListener;
import io.flutter.plugins.GeneratedPluginRegistrant;
import java.util.List;
import org.junit.After;
......@@ -251,6 +257,24 @@ public class FlutterActivityTest {
verify(mockDelegate, times(1)).onDetach();
}
@Test
public void itRestoresPluginStateBeforePluginOnCreate() {
FlutterLoader mockFlutterLoader = mock(FlutterLoader.class);
FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
when(mockFlutterJni.isAttached()).thenReturn(true);
FlutterEngine cachedEngine =
new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni);
FakeFlutterPlugin fakeFlutterPlugin = new FakeFlutterPlugin();
cachedEngine.getPlugins().add(fakeFlutterPlugin);
FlutterEngineCache.getInstance().put("my_cached_engine", cachedEngine);
Intent intent =
FlutterActivity.withCachedEngine("my_cached_engine").build(RuntimeEnvironment.application);
Robolectric.buildActivity(FlutterActivity.class, intent).setup();
assertTrue(
"Expected FakeFlutterPlugin onCreateCalled to be true", fakeFlutterPlugin.onCreateCalled);
}
static class FlutterActivityWithProvidedEngine extends FlutterActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
......@@ -272,6 +296,7 @@ public class FlutterActivityTest {
// This is just a compile time check to ensure that it's possible for FlutterActivity subclasses
// to provide their own intent builders which builds their own runtime types.
static class FlutterActivityWithIntentBuilders extends FlutterActivity {
public static NewEngineIntentBuilder withNewEngine() {
return new NewEngineIntentBuilder(FlutterActivityWithIntentBuilders.class);
}
......@@ -280,4 +305,59 @@ public class FlutterActivityTest {
return new CachedEngineIntentBuilder(FlutterActivityWithIntentBuilders.class, cachedEngineId);
}
}
private static final class FakeFlutterPlugin
implements FlutterPlugin,
ActivityAware,
OnSaveInstanceStateListener,
DefaultLifecycleObserver {
private ActivityPluginBinding activityPluginBinding;
private boolean stateRestored = false;
private boolean onCreateCalled = false;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
activityPluginBinding = binding;
binding.addOnSaveStateListener(this);
((FlutterActivity) binding.getActivity()).getLifecycle().addObserver(this);
}
@Override
public void onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity();
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}
@Override
public void onDetachedFromActivity() {
((FlutterActivity) activityPluginBinding.getActivity()).getLifecycle().removeObserver(this);
activityPluginBinding.removeOnSaveStateListener(this);
activityPluginBinding = null;
}
@Override
public void onSaveInstanceState(@NonNull Bundle bundle) {}
@Override
public void onRestoreInstanceState(@Nullable Bundle bundle) {
stateRestored = true;
}
@Override
public void onCreate(@NonNull LifecycleOwner lifecycleOwner) {
assertTrue("State was restored before onCreate", stateRestored);
onCreateCalled = true;
}
}
}
......@@ -81,7 +81,7 @@ public class FlutterAndroidComponentTest {
assertNotNull(binding.getTextureRegistry());
assertNotNull(binding.getPlatformViewRegistry());
delegate.onActivityCreated(null);
delegate.onRestoreInstanceState(null);
delegate.onCreateView(null, null, null);
delegate.onStart();
delegate.onResume();
......@@ -151,7 +151,7 @@ public class FlutterAndroidComponentTest {
assertNotNull(binding.getActivity());
assertNotNull(binding.getLifecycle());
delegate.onActivityCreated(null);
delegate.onRestoreInstanceState(null);
// Verify that after Activity creation, the plugin was allowed to restore state.
verify(mockSaveStateListener, times(1)).onRestoreInstanceState(any(Bundle.class));
......@@ -194,7 +194,7 @@ public class FlutterAndroidComponentTest {
// --- Execute the behavior under test ---
// Push the delegate through all lifecycle methods all the way to destruction.
delegate.onAttach(RuntimeEnvironment.application);
delegate.onActivityCreated(null);
delegate.onRestoreInstanceState(null);
delegate.onCreateView(null, null, null);
delegate.onStart();
delegate.onResume();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册