Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7aa4055

Browse files
authored
Release shim bindings when detaching (#13432)
1 parent f7d52fa commit 7aa4055

File tree

5 files changed

+155
-4
lines changed

5 files changed

+155
-4
lines changed

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ action("robolectric_tests") {
424424
"test/io/flutter/embedding/engine/FlutterJNITest.java",
425425
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
426426
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
427+
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
427428
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
428429
"test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java",
429430
"test/io/flutter/plugin/common/StandardMessageCodecTest.java",

shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import io.flutter.plugin.common.PluginRegistry;
2020

2121
/**
22-
* A {@link PluginRegistry} that is shimmed to use the new Android embedding and plugin API behind
23-
* the scenes.
22+
* A {@link PluginRegistry} that is shimmed to let old plugins use the new Android embedding and
23+
* plugin API behind the scenes.
2424
* <p>
2525
* The following is an example usage of {@code ShimPluginRegistry} within a {@code FlutterActivity}:
2626
* {@code
@@ -114,6 +114,7 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
114114
shimRegistrar.onDetachedFromEngine(binding);
115115
}
116116
flutterPluginBinding = null;
117+
activityPluginBinding = null;
117118
}
118119

119120
@Override
@@ -134,6 +135,7 @@ public void onDetachedFromActivityForConfigChanges() {
134135

135136
@Override
136137
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
138+
activityPluginBinding = binding;
137139
for (ShimRegistrar shimRegistrar : shimRegistrars) {
138140
shimRegistrar.onReattachedToActivityForConfigChanges(binding);
139141
}
@@ -144,6 +146,7 @@ public void onDetachedFromActivity() {
144146
for (ShimRegistrar shimRegistrar : shimRegistrars) {
145147
shimRegistrar.onDetachedFromActivity();
146148
}
149+
activityPluginBinding = null;
147150
}
148151
}
149152
}

shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import io.flutter.view.TextureRegistry;
2525

2626
/**
27-
* A {@link PluginRegistry.Registrar} that is shimmed to use the new Android embedding and plugin
28-
* API behind the scenes.
27+
* A {@link PluginRegistry.Registrar} that is shimmed let old plugins use the new Android embedding
28+
* and plugin API behind the scenes.
2929
* <p>
3030
* Instances of {@code ShimRegistrar}s are vended internally by a {@link ShimPluginRegistry}.
3131
*/
@@ -165,6 +165,7 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
165165
}
166166

167167
pluginBinding = null;
168+
activityPluginBinding = null;
168169
}
169170

170171
@Override

shell/platform/android/test/io/flutter/FlutterTestSuite.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.flutter.embedding.engine.FlutterEngineCacheTest;
1616
import io.flutter.embedding.engine.FlutterJNITest;
1717
import io.flutter.embedding.engine.RenderingComponentTest;
18+
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
1819
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
1920
import io.flutter.embedding.engine.systemchannels.PlatformChannelTest;
2021
import io.flutter.plugin.common.StandardMessageCodecTest;
@@ -40,6 +41,7 @@
4041
PreconditionsTest.class,
4142
RenderingComponentTest.class,
4243
StandardMessageCodecTest.class,
44+
ShimPluginRegistryTest.class,
4345
SingleViewPresentationTest.class,
4446
SmokeTest.class,
4547
TextInputPluginTest.class,
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package io.flutter.embedding.engine.plugins.shim;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.robolectric.RobolectricTestRunner;
7+
import org.robolectric.annotation.Config;
8+
9+
import android.app.Activity;
10+
import android.content.Context;
11+
import io.flutter.embedding.engine.FlutterEngine;
12+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
13+
import io.flutter.embedding.engine.plugins.PluginRegistry;
14+
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding;
15+
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
16+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
17+
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
18+
import io.flutter.plugin.common.PluginRegistry.Registrar;
19+
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertNotNull;
22+
import static org.junit.Assert.assertNull;
23+
import static org.mockito.Matchers.eq;
24+
import static org.mockito.Mockito.times;
25+
import static org.mockito.Mockito.verify;
26+
import static org.mockito.Mockito.when;
27+
28+
import org.mockito.ArgumentCaptor;
29+
import org.mockito.Mock;
30+
import org.mockito.MockitoAnnotations;
31+
32+
@Config(manifest=Config.NONE)
33+
@RunWith(RobolectricTestRunner.class)
34+
public class ShimPluginRegistryTest {
35+
36+
@Mock private FlutterEngine mockFlutterEngine;
37+
@Mock private FlutterPluginBinding mockFlutterPluginBinding;
38+
@Mock private ActivityPluginBinding mockActivityPluginBinding;
39+
@Mock private PluginRegistry mockPluginRegistry;
40+
@Mock private Context mockApplicationContext;
41+
@Mock private Activity mockActivity;
42+
43+
@Before
44+
public void setup() {
45+
MockitoAnnotations.initMocks(this);
46+
when(mockFlutterEngine.getPlugins()).thenReturn(mockPluginRegistry);
47+
when(mockFlutterPluginBinding.getApplicationContext()).thenReturn(mockApplicationContext);
48+
when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity);
49+
}
50+
51+
@Test
52+
public void itSuppliesOldAPIsViaTheNewFlutterPluginBinding() {
53+
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
54+
// This is the consumption side of the old plugins.
55+
Registrar registrarUnderTest = registryUnderTest.registrarFor("test");
56+
57+
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor = ArgumentCaptor.forClass(FlutterPlugin.class);
58+
// A single shim aggregate was added as a new plugin to the FlutterEngine's PluginRegistry.
59+
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
60+
// This is really a ShimRegistrarAggregate acting as a FlutterPlugin which is the
61+
// intermediate consumption side of the new plugin inside the shim.
62+
FlutterPlugin shimAggregateUnderTest = shimAggregateCaptor.getValue();
63+
// The FlutterPluginBinding is the supply side of the new plugin.
64+
shimAggregateUnderTest.onAttachedToEngine(mockFlutterPluginBinding);
65+
66+
// Consume something from the old plugin API.
67+
assertEquals(mockApplicationContext, registrarUnderTest.context());
68+
// Check that the value comes from the supply side of the new plugin.
69+
verify(mockFlutterPluginBinding).getApplicationContext();
70+
}
71+
72+
@Test
73+
public void itSuppliesMultipleOldPlugins() {
74+
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
75+
Registrar registrarUnderTest1 = registryUnderTest.registrarFor("test1");
76+
Registrar registrarUnderTest2 = registryUnderTest.registrarFor("test2");
77+
78+
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor = ArgumentCaptor.forClass(FlutterPlugin.class);
79+
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
80+
// There's only one aggregate for many old plugins.
81+
FlutterPlugin shimAggregateUnderTest = shimAggregateCaptor.getValue();
82+
83+
// The FlutterPluginBinding is the supply side of the new plugin.
84+
shimAggregateUnderTest.onAttachedToEngine(mockFlutterPluginBinding);
85+
86+
// Since the 2 old plugins are supplied by the same intermediate FlutterPlugin, they should
87+
// get the same value.
88+
assertEquals(registrarUnderTest1.context(), registrarUnderTest2.context());
89+
verify(mockFlutterPluginBinding, times(2)).getApplicationContext();
90+
}
91+
92+
@Test
93+
public void itCanOnlySupplyActivityBindingWhenUpstreamActivityIsAttached() {
94+
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
95+
Registrar registrarUnderTest = registryUnderTest.registrarFor("test");
96+
97+
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor = ArgumentCaptor.forClass(FlutterPlugin.class);
98+
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
99+
FlutterPlugin shimAggregateAsPlugin = shimAggregateCaptor.getValue();
100+
ActivityAware shimAggregateAsActivityAware = (ActivityAware) shimAggregateCaptor.getValue();
101+
102+
// Nothing is retrievable when nothing is attached.
103+
assertNull(registrarUnderTest.context());
104+
assertNull(registrarUnderTest.activity());
105+
106+
shimAggregateAsPlugin.onAttachedToEngine(mockFlutterPluginBinding);
107+
108+
assertEquals(mockApplicationContext, registrarUnderTest.context());
109+
assertNull(registrarUnderTest.activity());
110+
111+
shimAggregateAsActivityAware.onAttachedToActivity(mockActivityPluginBinding);
112+
113+
// Now context is the activity context.
114+
assertEquals(mockActivity, registrarUnderTest.activeContext());
115+
assertEquals(mockActivity, registrarUnderTest.activity());
116+
117+
shimAggregateAsActivityAware.onDetachedFromActivityForConfigChanges();
118+
119+
assertEquals(mockApplicationContext, registrarUnderTest.activeContext());
120+
assertNull(registrarUnderTest.activity());
121+
122+
shimAggregateAsActivityAware.onReattachedToActivityForConfigChanges(mockActivityPluginBinding);
123+
assertEquals(mockActivity, registrarUnderTest.activeContext());
124+
assertEquals(mockActivity, registrarUnderTest.activity());
125+
126+
shimAggregateAsActivityAware.onDetachedFromActivity();
127+
128+
assertEquals(mockApplicationContext, registrarUnderTest.activeContext());
129+
assertNull(registrarUnderTest.activity());
130+
131+
// Attach an activity again.
132+
shimAggregateAsActivityAware.onAttachedToActivity(mockActivityPluginBinding);
133+
134+
assertEquals(mockActivity, registrarUnderTest.activeContext());
135+
assertEquals(mockActivity, registrarUnderTest.activity());
136+
137+
// Now rip out the whole engine.
138+
shimAggregateAsPlugin.onDetachedFromEngine(mockFlutterPluginBinding);
139+
140+
// And everything should have been made unavailable.
141+
assertNull(registrarUnderTest.activeContext());
142+
assertNull(registrarUnderTest.activity());
143+
}
144+
}

0 commit comments

Comments
 (0)