Skip to content

Commit 4e87f60

Browse files
authored
Read loading unit mapping from AndroidManifest instead of strings (flutter#23868)
1 parent a5c305e commit 4e87f60

File tree

2 files changed

+69
-12
lines changed

2 files changed

+69
-12
lines changed

shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66

77
import android.annotation.SuppressLint;
88
import android.content.Context;
9+
import android.content.pm.ApplicationInfo;
10+
import android.content.pm.PackageManager;
911
import android.content.pm.PackageManager.NameNotFoundException;
1012
import android.content.res.AssetManager;
1113
import android.os.Build;
14+
import android.os.Bundle;
1215
import android.util.SparseArray;
1316
import android.util.SparseIntArray;
1417
import androidx.annotation.NonNull;
@@ -53,6 +56,8 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
5356
private @NonNull SparseArray<String> sessionIdToState;
5457
private @NonNull Map<String, Integer> nameToSessionId;
5558

59+
protected @NonNull SparseArray<String> loadingUnitIdToModuleNames;
60+
5661
private FeatureInstallStateUpdatedListener listener;
5762

5863
private class FeatureInstallStateUpdatedListener implements SplitInstallStateUpdatedListener {
@@ -202,6 +207,9 @@ public PlayStoreDeferredComponentManager(
202207
sessionIdToLoadingUnitId = new SparseIntArray();
203208
sessionIdToState = new SparseArray<>();
204209
nameToSessionId = new HashMap<>();
210+
211+
loadingUnitIdToModuleNames = new SparseArray<>();
212+
initLoadingUnitMappingToModuleNames();
205213
}
206214

207215
public void setJNI(@NonNull FlutterJNI flutterJNI) {
@@ -222,19 +230,49 @@ public void setDeferredComponentChannel(DeferredComponentChannel channel) {
222230
this.channel = channel;
223231
}
224232

225-
private String loadingUnitIdToModuleName(int loadingUnitId) {
226-
// Loading unit id to module name mapping stored in android Strings
227-
// resources.
228-
int moduleNameIdentifier =
229-
context
230-
.getResources()
231-
.getIdentifier("loadingUnit" + loadingUnitId, "string", context.getPackageName());
232-
return context.getResources().getString(moduleNameIdentifier);
233+
@NonNull
234+
private ApplicationInfo getApplicationInfo() {
235+
try {
236+
return context
237+
.getPackageManager()
238+
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
239+
} catch (NameNotFoundException e) {
240+
throw new RuntimeException(e);
241+
}
242+
}
243+
244+
// Obtain and parses the metadata string. An example encoded string is:
245+
//
246+
// "2:module2,3:module3,4:module1"
247+
//
248+
// Where loading unit 2 is included in module2, loading unit 3 is
249+
// included in module3, and loading unit 4 is included in module1.
250+
private void initLoadingUnitMappingToModuleNames() {
251+
String mappingKey = DeferredComponentManager.class.getName() + ".loadingUnitMapping";
252+
ApplicationInfo applicationInfo = getApplicationInfo();
253+
if (applicationInfo != null) {
254+
Bundle metaData = applicationInfo.metaData;
255+
if (metaData != null) {
256+
String rawMappingString = metaData.getString(mappingKey, null);
257+
if (rawMappingString == null) {
258+
Log.e(
259+
TAG,
260+
"No loading unit to dynamic feature module name found. Ensure '"
261+
+ mappingKey
262+
+ "' is defined in the base module's AndroidManifest.");
263+
} else {
264+
for (String entry : rawMappingString.split(",")) {
265+
String[] splitEntry = entry.split(":");
266+
loadingUnitIdToModuleNames.put(Integer.parseInt(splitEntry[0]), splitEntry[1]);
267+
}
268+
}
269+
}
270+
}
233271
}
234272

235273
public void installDeferredComponent(int loadingUnitId, String moduleName) {
236274
String resolvedModuleName =
237-
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
275+
moduleName != null ? moduleName : loadingUnitIdToModuleNames.get(loadingUnitId);
238276
if (resolvedModuleName == null) {
239277
Log.e(
240278
TAG,
@@ -297,7 +335,7 @@ public void installDeferredComponent(int loadingUnitId, String moduleName) {
297335

298336
public String getDeferredComponentInstallState(int loadingUnitId, String moduleName) {
299337
String resolvedModuleName =
300-
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
338+
moduleName != null ? moduleName : loadingUnitIdToModuleNames.get(loadingUnitId);
301339
if (resolvedModuleName == null) {
302340
Log.e(
303341
TAG,
@@ -400,7 +438,7 @@ public void loadDartLibrary(int loadingUnitId, String moduleName) {
400438

401439
public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) {
402440
String resolvedModuleName =
403-
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
441+
moduleName != null ? moduleName : loadingUnitIdToModuleNames.get(loadingUnitId);
404442
if (resolvedModuleName == null) {
405443
Log.e(
406444
TAG,
@@ -410,7 +448,9 @@ public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName)
410448
List<String> modulesToUninstall = new ArrayList<>();
411449
modulesToUninstall.add(resolvedModuleName);
412450
splitInstallManager.deferredUninstall(modulesToUninstall);
413-
sessionIdToState.delete(nameToSessionId.get(resolvedModuleName));
451+
if (nameToSessionId.get(resolvedModuleName) != null) {
452+
sessionIdToState.delete(nameToSessionId.get(resolvedModuleName));
453+
}
414454
return true;
415455
}
416456

shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.flutter.embedding.engine.deferredcomponents;
66

77
import static junit.framework.TestCase.assertEquals;
8+
import static junit.framework.TestCase.assertFalse;
89
import static junit.framework.TestCase.assertTrue;
910
import static org.mockito.Mockito.any;
1011
import static org.mockito.Mockito.anyInt;
@@ -19,6 +20,7 @@
1920
import android.content.pm.PackageManager.NameNotFoundException;
2021
import android.content.res.AssetManager;
2122
import android.os.Bundle;
23+
import android.util.SparseArray;
2224
import androidx.annotation.NonNull;
2325
import io.flutter.embedding.engine.FlutterJNI;
2426
import io.flutter.embedding.engine.loader.ApplicationInfoLoader;
@@ -70,6 +72,9 @@ public void deferredComponentInstallFailure(
7072
private class TestPlayStoreDeferredComponentManager extends PlayStoreDeferredComponentManager {
7173
public TestPlayStoreDeferredComponentManager(Context context, FlutterJNI jni) {
7274
super(context, jni);
75+
loadingUnitIdToModuleNames = new SparseArray<>();
76+
loadingUnitIdToModuleNames.put(5, "FakeModuleName5");
77+
loadingUnitIdToModuleNames.put(2, "FakeModuleName2");
7378
}
7479

7580
@Override
@@ -223,4 +228,16 @@ public void stateGetterReturnsUnknowByDefault() throws NameNotFoundException {
223228
new TestPlayStoreDeferredComponentManager(spyContext, jni);
224229
assertEquals(playStoreManager.getDeferredComponentInstallState(-1, "invalidName"), "unknown");
225230
}
231+
232+
@Test
233+
public void loadingUnitMappingFindsMatch() throws NameNotFoundException {
234+
TestFlutterJNI jni = new TestFlutterJNI();
235+
Context spyContext = spy(RuntimeEnvironment.application);
236+
TestPlayStoreDeferredComponentManager playStoreManager =
237+
new TestPlayStoreDeferredComponentManager(spyContext, jni);
238+
239+
assertTrue(playStoreManager.uninstallDeferredComponent(5, null));
240+
assertTrue(playStoreManager.uninstallDeferredComponent(2, null));
241+
assertFalse(playStoreManager.uninstallDeferredComponent(3, null));
242+
}
226243
}

0 commit comments

Comments
 (0)