Поскольку нельзя ссылаться на действия функционального модуля в AndroidManifest.xml
базового модуля, я написал SplitInstallActivity
, который находится в debug
исходном наборе базового модуля, где он также доступен для тестов. Его можно вызвать с конфигурацией запуска, которая передает флаги запуска:
-e "moduleName" "feature_module" -e "className" "com.acme.feature.SomeActivity"
Он либо устанавливает функциональный модуль до moduleName
, и / или запускает Activity
до className
.
По крайней мере, это работает при развертывании «APK по умолчанию» вместо «APK из набора приложений».
ArgumentKeys.java
public class ArgumentKeys {
/** {@link SplitInstallActivity} dynamic features, the module name */
public static final String ARGUMENT_FEATURE_MODULE_MODULE_NAME = "moduleName";
/** {@link SplitInstallActivity} dynamic features, the activity class name to launch */
public static final String ARGUMENT_FEATURE_MODULE_CLASS_NAME = "className";
}
SplitInstallActivity.java
/**
* Split-Install {@link AppCompatActivity}.
* @author Martin Zeitler
**/
public class SplitInstallActivity extends AppCompatActivity implements SplitInstallStateUpdatedListener {
private static final String LOG_TAG = SplitInstallActivity.class.getSimpleName();
private SplitInstallRequest request;
private SplitInstallManager sim;
private String moduleName;
private String className;
public SplitInstallActivity() {}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* instance the {@link SplitInstallManager}: */
this.sim = SplitInstallManagerFactory.create(this.getApplicationContext());
/* obtain the feature module & class name from arguments */
if(this.getIntent() != null) {
Bundle extras = this.getIntent().getExtras();
if(extras != null) {
this.moduleName = extras.getString(ArgumentKeys.ARGUMENT_FEATURE_MODULE_MODULE_NAME);
this.className = extras.getString(ArgumentKeys.ARGUMENT_FEATURE_MODULE_CLASS_NAME);
if(this.moduleName != null && this.className != null) {
this.startFeatureActivity(this.moduleName, this.className);
} else {
Log.e(LOG_TAG, "module and class are required.");
}
}
}
}
/** it listens for the split-install session state */
@Override
public void onStateUpdate(SplitInstallSessionState state) {
if(state.errorCode() == SplitInstallErrorCode.NO_ERROR && state.status() == SplitInstallSessionStatus.INSTALLED) {
Log.d(LOG_TAG, "dynamic feature " + this.moduleName + " had been installed.");
this.startFeatureActivity(this.moduleName, this.className);
} else {
// this.OnSplitInstallStatus(state);
}
}
/** it checks if the dynamic feature module is installed and then either installs it - or starts the desired activity */
private void startFeatureActivity(@NonNull String moduleName, @NonNull String className) {
if (this.sim.getInstalledModules().contains(moduleName)) {
Log.d(LOG_TAG, "dynamic feature module " + moduleName + " already installed.");
Intent intent = this.getIntent();
intent.setClassName(BuildConfig.APPLICATION_ID, className);
this.startActivity(intent);
this.finish();
} else {
Log.d(LOG_TAG, "dynamic feature module " + moduleName + " is not installed.");
this.installFeatureModule(moduleName);
}
}
/** it installs a dynamic feature module on demand */
private void installFeatureModule(@NonNull String moduleName) {
Log.d(LOG_TAG, "dynamic feature module " + moduleName + " will be installed.");
this.request = SplitInstallRequest.newBuilder().addModule(moduleName).build();
this.sim.registerListener(this);
this.sim.startInstall(this.request);
}
...
}
Запуск определенного Activity
можно автоматизировать с помощью ActivityTestRule<?>
:
@Rule
public ActivityTestRule<SplitInstallActivity> mRule = new ActivityTestRule<SplitInstallActivity>(SplitInstallActivity.class) {
@Override
protected Intent getActivityIntent() {
Intent intent = new Intent();
Bundle extras = new Bundle();
extras.putString(ArgumentKeys.ARGUMENT_FEATURE_MODULE_MODULE_NAME, "feature_module");
extras.putString(ArgumentKeys.ARGUMENT_FEATURE_MODULE_CLASS_NAME, "com.acme.feature.SomeActivity");
intent.putExtras(extras);
return intent;
}
};
Для тестирования необходимо указать эти зависимости в build.gradle
функционального модуля:
androidTestDebugImplementation "com.google.android.gms:play-services-basement:17.1.1"
androidTestDebugImplementation "com.google.android.play:core:1.6.4"
В противном случае он не может связать ресурсы тестового приложения и не работает:
> Task :feature_module:processDebugAndroidTestResources FAILED
AGPBI: {"kind":"error","text":"Android resource linking failed","sources":[{"file":"/home/user/.gradle/caches/transforms-2/files-2.1/7435b27a13269cffdd35a7dd69f0b9d2/core-1.6.4/AndroidManifest.xml","position":{"startLine":8,"startColumn":4,"endColumn":277}}],"original":"/home/user/.gradle/caches/transforms-2/files-2.1/7435b27a13269cffdd35a7dd69f0b9d2/core-1.6.4/AndroidManifest.xml:9:5-278: AAPT: error: resource style/Theme.PlayCore.Transparent (aka com.acme.feature.test:style/Theme.PlayCore.Transparent) not found.","tool":"AAPT"}
AGPBI: {"kind":"error","text":"Android resource linking failed","sources":[{"file":"/home/user/.gradle/caches/transforms-2/files-2.1/c1b8b45e2f49fbe83ea45d80000bd6e9/jetified-play-services-basement-17.0.0/AndroidManifest.xml","position":{"startLine":22,"startColumn":8,"endLine":24,"endColumn":68}}],"original":"/home/user/.gradle/caches/transforms-2/files-2.1/c1b8b45e2f49fbe83ea45d80000bd6e9/jetified-play-services-basement-17.0.0/AndroidManifest.xml:23:9-25:69: AAPT: error: resource integer/google_play_services_version (aka com.acme.feature.test:integer/google_play_services_version) not found.","tool":"AAPT"}
Для тестирования также есть:
person
Martin Zeitler
schedule
30.10.2019
Bundles
функции (которая, я думаю, как-то связана с тем же вопросом, потому что вам нужно как можно больше разделить все по разным пакетам). Они сказали, что прямо сейчас вы можете вызвать Activity из другого пакета с отражением и у вас есть несколько обходных путей для этого (на самом деле такое же отражение, но более приятным способом). - person GensaGames   schedule 29.10.2019AndroidManifest.xml
также не разрешается пространство имен. - person Martin Zeitler   schedule 29.10.2019How to run the activities from dynamic features modules?
- person GensaGames   schedule 29.10.2019run-configuration
(поскольку подход времени выполнения задокументирован). Одним из возможных обходных путей может быть добавление действия отладки с фильтром намерения для имени класса, который затем запускает эти действия модуля функции во время выполнения через отражение, но запуск их сActitivtiyRule
для автоматического тестирования все еще кажется сложным. Также возможно созданиеIntent
для рабочей конфигурации вручную. - person Martin Zeitler   schedule 29.10.2019bundletool
, который в основном просто устанавливает все модули один за другим. В зависимости от комплектации запустить. Вот документация по этому инструменту developer.android.com/studio/command-line/bundletool < / а> - person GensaGames   schedule 29.10.2019Activity
сIntent
из конфигурации выполнения. - person Martin Zeitler   schedule 30.10.2019