mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Allow naming shared libraries in deferred component via AndroidManifest (flutter/engine#23925)
This commit is contained in:
parent
849563f41b
commit
b9fbeca5b3
@ -44,6 +44,9 @@ import java.util.Queue;
|
||||
public class PlayStoreDeferredComponentManager implements DeferredComponentManager {
|
||||
private static final String TAG = "PlayStoreDeferredComponentManager";
|
||||
|
||||
public static final String MAPPING_KEY =
|
||||
DeferredComponentManager.class.getName() + ".loadingUnitMapping";
|
||||
|
||||
private @NonNull SplitInstallManager splitInstallManager;
|
||||
private @Nullable FlutterJNI flutterJNI;
|
||||
private @Nullable DeferredComponentChannel channel;
|
||||
@ -57,6 +60,7 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
|
||||
private @NonNull Map<String, Integer> nameToSessionId;
|
||||
|
||||
protected @NonNull SparseArray<String> loadingUnitIdToModuleNames;
|
||||
protected @NonNull SparseArray<String> loadingUnitIdToSharedLibraryNames;
|
||||
|
||||
private FeatureInstallStateUpdatedListener listener;
|
||||
|
||||
@ -209,6 +213,7 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
|
||||
nameToSessionId = new HashMap<>();
|
||||
|
||||
loadingUnitIdToModuleNames = new SparseArray<>();
|
||||
loadingUnitIdToSharedLibraryNames = new SparseArray<>();
|
||||
initLoadingUnitMappingToModuleNames();
|
||||
}
|
||||
|
||||
@ -243,27 +248,33 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
|
||||
|
||||
// Obtain and parses the metadata string. An example encoded string is:
|
||||
//
|
||||
// "2:module2,3:module3,4:module1"
|
||||
// "2:module2,3:module3,4:module1:libmodule4.so"
|
||||
//
|
||||
// Where loading unit 2 is included in module2, loading unit 3 is
|
||||
// included in module3, and loading unit 4 is included in module1.
|
||||
// An optional third parameter can be added to indicate the name of
|
||||
// the shared library of the loading unit.
|
||||
private void initLoadingUnitMappingToModuleNames() {
|
||||
String mappingKey = DeferredComponentManager.class.getName() + ".loadingUnitMapping";
|
||||
ApplicationInfo applicationInfo = getApplicationInfo();
|
||||
if (applicationInfo != null) {
|
||||
Bundle metaData = applicationInfo.metaData;
|
||||
if (metaData != null) {
|
||||
String rawMappingString = metaData.getString(mappingKey, null);
|
||||
String rawMappingString = metaData.getString(MAPPING_KEY, null);
|
||||
if (rawMappingString == null) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"No loading unit to dynamic feature module name found. Ensure '"
|
||||
+ mappingKey
|
||||
+ MAPPING_KEY
|
||||
+ "' is defined in the base module's AndroidManifest.");
|
||||
} else {
|
||||
for (String entry : rawMappingString.split(",")) {
|
||||
String[] splitEntry = entry.split(":");
|
||||
loadingUnitIdToModuleNames.put(Integer.parseInt(splitEntry[0]), splitEntry[1]);
|
||||
int loadingUnitId = Integer.parseInt(splitEntry[0]);
|
||||
loadingUnitIdToModuleNames.put(loadingUnitId, splitEntry[1]);
|
||||
if (splitEntry.length > 2) {
|
||||
loadingUnitIdToSharedLibraryNames.put(loadingUnitId, splitEntry[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -379,9 +390,12 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
|
||||
return;
|
||||
}
|
||||
|
||||
// This matches/depends on dart's loading unit naming convention, which we use unchanged.
|
||||
String aotSharedLibraryName =
|
||||
flutterApplicationInfo.aotSharedLibraryName + "-" + loadingUnitId + ".part.so";
|
||||
String aotSharedLibraryName = loadingUnitIdToSharedLibraryNames.get(loadingUnitId);
|
||||
if (aotSharedLibraryName == null) {
|
||||
// If the filename is not specified, we use dart's loading unit naming convention.
|
||||
aotSharedLibraryName =
|
||||
flutterApplicationInfo.aotSharedLibraryName + "-" + loadingUnitId + ".part.so";
|
||||
}
|
||||
|
||||
// Possible values: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64
|
||||
String abi;
|
||||
|
||||
@ -20,7 +20,6 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.SparseArray;
|
||||
import androidx.annotation.NonNull;
|
||||
import io.flutter.embedding.engine.FlutterJNI;
|
||||
import io.flutter.embedding.engine.loader.ApplicationInfoLoader;
|
||||
@ -72,9 +71,6 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
private class TestPlayStoreDeferredComponentManager extends PlayStoreDeferredComponentManager {
|
||||
public TestPlayStoreDeferredComponentManager(Context context, FlutterJNI jni) {
|
||||
super(context, jni);
|
||||
loadingUnitIdToModuleNames = new SparseArray<>();
|
||||
loadingUnitIdToModuleNames.put(5, "FakeModuleName5");
|
||||
loadingUnitIdToModuleNames.put(2, "FakeModuleName2");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -85,11 +81,25 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private Context createSpyContext(Bundle metadata) throws NameNotFoundException {
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
if (metadata == null) {
|
||||
metadata = new Bundle();
|
||||
}
|
||||
PackageManager packageManager = mock(PackageManager.class);
|
||||
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
|
||||
applicationInfo.metaData = metadata;
|
||||
when(packageManager.getApplicationInfo(any(String.class), any(int.class)))
|
||||
.thenReturn(applicationInfo);
|
||||
doReturn(packageManager).when(spyContext).getPackageManager();
|
||||
return spyContext;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadCallsJNIFunctions() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
Context spyContext = createSpyContext(null);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
String soTestFilename = "libapp.so-123.part.so";
|
||||
String soTestPath = "test/path/" + soTestFilename;
|
||||
@ -114,19 +124,13 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
@Test
|
||||
public void downloadCallsJNIFunctionsWithFilenameFromManifest() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(ApplicationInfoLoader.PUBLIC_AOT_SHARED_LIBRARY_NAME, "custom_name.so");
|
||||
bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
|
||||
PackageManager packageManager = mock(PackageManager.class);
|
||||
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
|
||||
applicationInfo.metaData = bundle;
|
||||
when(packageManager.getApplicationInfo(any(String.class), any(int.class)))
|
||||
.thenReturn(applicationInfo);
|
||||
doReturn(packageManager).when(spyContext).getPackageManager();
|
||||
|
||||
Context spyContext = createSpyContext(bundle);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
|
||||
String soTestFilename = "custom_name.so-123.part.so";
|
||||
String soTestPath = "test/path/" + soTestFilename;
|
||||
@ -148,11 +152,42 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
assertEquals(jni.assetBundlePath, "custom_assets");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadCallsJNIFunctionsWithSharedLibraryNameFromManifest()
|
||||
throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so");
|
||||
bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
|
||||
|
||||
Context spyContext = createSpyContext(bundle);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
|
||||
String soTestFilename = "custom_name.so";
|
||||
String soTestPath = "test/path/" + soTestFilename;
|
||||
doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
|
||||
TestPlayStoreDeferredComponentManager playStoreManager =
|
||||
new TestPlayStoreDeferredComponentManager(spyContext, jni);
|
||||
jni.setDeferredComponentManager(playStoreManager);
|
||||
assertEquals(jni.loadingUnitId, 0);
|
||||
|
||||
playStoreManager.installDeferredComponent(123, "TestModuleName");
|
||||
assertEquals(jni.loadDartDeferredLibraryCalled, 1);
|
||||
assertEquals(jni.updateAssetManagerCalled, 1);
|
||||
assertEquals(jni.deferredComponentInstallFailureCalled, 0);
|
||||
|
||||
assertEquals(jni.searchPaths[0], soTestFilename);
|
||||
assertTrue(jni.searchPaths[1].endsWith(soTestPath));
|
||||
assertEquals(jni.searchPaths.length, 2);
|
||||
assertEquals(jni.loadingUnitId, 123);
|
||||
assertEquals(jni.assetBundlePath, "custom_assets");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchPathsAddsApks() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
Context spyContext = createSpyContext(null);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
String apkTestPath = "test/path/TestModuleName_armeabi_v7a.apk";
|
||||
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
|
||||
@ -176,8 +211,7 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
@Test
|
||||
public void invalidSearchPathsAreIgnored() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
Context spyContext = createSpyContext(null);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
String apkTestPath = "test/path/invalidpath.apk";
|
||||
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
|
||||
@ -200,8 +234,8 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
@Test
|
||||
public void assetManagerUpdateInvoked() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
Context spyContext = createSpyContext(null);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
AssetManager assetManager = spyContext.getAssets();
|
||||
String apkTestPath = "blah doesn't matter here";
|
||||
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
|
||||
@ -222,8 +256,8 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
@Test
|
||||
public void stateGetterReturnsUnknowByDefault() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
|
||||
Context spyContext = createSpyContext(null);
|
||||
doReturn(null).when(spyContext).getAssets();
|
||||
TestPlayStoreDeferredComponentManager playStoreManager =
|
||||
new TestPlayStoreDeferredComponentManager(spyContext, jni);
|
||||
assertEquals(playStoreManager.getDeferredComponentInstallState(-1, "invalidName"), "unknown");
|
||||
@ -232,7 +266,9 @@ public class PlayStoreDeferredComponentManagerTest {
|
||||
@Test
|
||||
public void loadingUnitMappingFindsMatch() throws NameNotFoundException {
|
||||
TestFlutterJNI jni = new TestFlutterJNI();
|
||||
Context spyContext = spy(RuntimeEnvironment.application);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(PlayStoreDeferredComponentManager.MAPPING_KEY, "2:module1,5:module2");
|
||||
Context spyContext = createSpyContext(bundle);
|
||||
TestPlayStoreDeferredComponentManager playStoreManager =
|
||||
new TestPlayStoreDeferredComponentManager(spyContext, jni);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user