на Android 11, 12, 13, 14 и 15 Все работает нормально: загрузки модулей, изменения состояния в установленные и Splitinstallmanager.getInstalledModules () возвращает ожидаемый модуль. splitinstallmanager.getInstalledModules () всегда возвращает пустой набор [], независимо от того, как долго я жду. < /p>
Вот пример журнала от Android 10: < /p>
Код: Выделить всё
checkInstallFt > featureToInstall : feature_EN
splitInstallMan > SUCCESS with Session ID: 11 / 11
Status PENDING 1
Status DOWNLOADING 2
DOWNLOADING: 3050366 / 3050366
Status INSTALLING 4
Status INSTALLED 5
INSTALLED: 3050366 / 3050366
installedModules: []
installedModules: []
installedModules: []
< /code>
Appactivity.java:
import com.google.android.play.core.splitinstall.SplitInstallException;
import com.google.android.play.core.splitinstall.SplitInstallManager;
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
import com.google.android.play.core.splitinstall.SplitInstallRequest;
import com.google.android.play.core.splitinstall.SplitInstallSessionState;
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener;
import com.google.android.play.core.splitinstall.SplitInstallHelper;
import com.google.android.play.core.splitinstall.model.SplitInstallErrorCode;
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
// =============== SPLITTER BEGIN ================
static private SplitInstallManager splitInstallManager = null;
static private SplitInstallStateUpdatedListener listener = null;
// Initializes a variable to later track the session ID for a given request.
static int mySessionId = 0;
static String _LANG__CODE = "EN";
static String featureToInstall = "feature_" + _LANG__CODE;
static boolean logActive = true;
static void debugLog(String msg) {
if (logActive) Log.d("LOG", msg);
};
private static void waitUntilAssetsReady(String langCode) {
Handler handler = new Handler(Looper.getMainLooper());
Runnable checker = new Runnable() {
@Override
public void run() {
debugLog("waitUntilAssetsReady");
try {
Context newContext = activity.getApplicationContext().createPackageContext(
activity.getApplicationContext().getPackageName(), 0);
AssetManager am = newContext.getAssets();
String[] listAssets = am.list("gfx/assets_" + langCode);
if (listAssets != null && listAssets.length > 0) {
debugLog("Assets ready: gfx/assets_" + langCode);
refreshAssetManager();
onMultilanguageDownloaded();
return;
}
} catch (Exception e) {
e.printStackTrace();
}
handler.postDelayed(this, 500);
}
};
handler.post(checker);
}
private static void waitUntilModuleInstalled(String featureToInstall) {
Handler handler = new Handler(Looper.getMainLooper());
Runnable checker = new Runnable() {
@Override
public void run() {
Set installedModules = splitInstallManager.getInstalledModules();
debugLog("installedModules: " + installedModules.toString());
if (installedModules.contains(featureToInstall)) {
Context newContext = null;
try {
newContext = activity.getApplicationContext().createPackageContext(activity.getApplicationContext().getPackageName(), 0);
AssetManager am = newContext.getAssets();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
debugLog("installedModules with Error: " + e.getMessage());
}
waitUntilAssetsReady(_LANG__CODE);
debugLog("Module found & callback executed");
} else {
handler.postDelayed(this, 500);
}
}
};
handler.post(checker);
}
static void addUpdateListener() {
// Creates a listener for request status updates.
listener = new SplitInstallStateUpdatedListener() {
@SuppressLint("SwitchIntDef")
@Override
public void onStateUpdate(SplitInstallSessionState sessionState) {
if (sessionState.status() == SplitInstallSessionStatus.FAILED &&
sessionState.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
// Retry the request.
debugLog("onStateUpdate > sessionState.status() == SplitInstallSessionStatus.FAILED");
showDownloadFailed();
return;
}
if (sessionState.sessionId() == mySessionId) {
int totalBytes = (int) sessionState.totalBytesToDownload();
int progress = (int) sessionState.bytesDownloaded();
switch (sessionState.status()) {
case SplitInstallSessionStatus.UNKNOWN: // 0
debugLog("Status UNKNOWN " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.PENDING: // 1
debugLog("Status PENDING " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.DOWNLOADING: // 2
// Update progress bar.
debugLog("Status DOWNLOADING " + String.valueOf(sessionState.status()));
debugLog("DOWNLOADING: " + String.valueOf(progress) + " / " + String.valueOf(totalBytes));
break;
case SplitInstallSessionStatus.DOWNLOADED: // 3
debugLog("Status DOWNLOADED " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.INSTALLING: // 4
debugLog("Status INSTALLING " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.INSTALLED: // 5
debugLog("Status INSTALLED " + String.valueOf(sessionState.status()));
debugLog("INSTALLED: " + String.valueOf(progress) + " / " + String.valueOf(totalBytes));
unregisterListener();
waitUntilModuleInstalled(featureToInstall);
break;
case SplitInstallSessionStatus.FAILED: // 6
debugLog("Status FAILED " + String.valueOf(sessionState.status()));
debugLog("Status FAILED with error: " + sessionState.errorCode());
case SplitInstallSessionStatus.CANCELED: // 7
debugLog("Status CANCELED " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION: //8
debugLog("Status REQUIRES_USER_CONFIRMATION, user action required " + String.valueOf(sessionState.status()));
case SplitInstallSessionStatus.CANCELING: // 9
debugLog("Status CANCELING " + String.valueOf(sessionState.status()));
default:
debugLog("updateListener not downloading or installed with code: " + String.valueOf(sessionState.status()));
}
}
}
};
// Registers the listener.
splitInstallManager.registerListener(listener);
}
static void checkForActiveDownloads() {
// Returns a SplitInstallSessionState object for each active session as a List.
splitInstallManager
.getSessionStates()
.addOnCompleteListener(new OnCompleteListener[*]>() {
@Override
public void onComplete(Task task) {
if (task.isSuccessful()) {
// Check for active sessions.
for (SplitInstallSessionState state : task.getResult()) {
if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
// Cancels the request for the given session ID.
debugLog("checkForActiveDownloads Cancel the request, or request a deferred installation");
splitInstallManager.cancelInstall(mySessionId);
}
}
}
}
});
}
static void showDownloadFailed() {
unregisterListener();
checkInstallFeature(_LANG__CODE);
}
static void unregisterListener() {
if (listener == null) {
debugLog("unregListener listener == null");
return;
}
splitInstallManager.unregisterListener(listener);
}
public static void refreshAssetManager() {
debugLog("refreshAssetManager - 1");
SplitInstallHelper.updateAppInfo(activity.getApplicationContext());
Cocos2dxHelper.refreshAssetManager(activity.getApplicationContext());
debugLog("refreshAssetManager - 2");
}
@SuppressLint("SwitchIntDef")
public static void checkInstallFeature(String LANG_CODE) {
if (activity == null) {
debugLog("checkInstallFt > activity == null");
return;
}
_LANG__CODE = LANG_CODE;
featureToInstall = "feature_" + _LANG__CODE;
debugLog("checkInstallFt > featureToInstall : " + featureToInstall);
Set installedModules = splitInstallManager.getInstalledModules();
if (installedModules.contains(featureToInstall)) {
debugLog("Module already installed: " + featureToInstall);
onMultilanguageDownloaded();
return;
}
//if (featureToInstall.equals("feature_EN") || featureToInstall.equals("feature_IN")) return;
// Builds a request to install the feature module
SplitInstallRequest request = SplitInstallRequest.newBuilder()
// You can download multiple on demand modules per
// request by invoking the following method for each
// module you want to install.
.addModule(featureToInstall)
.build();
// Begin the installation of the feature1 module and handle success/failure
splitInstallManager
.startInstall(request)
.addOnSuccessListener(new OnSuccessListener() {
@Override
public void onSuccess(Integer integer) {
// Module download successful
mySessionId = integer;
debugLog("splitInstallMan > SUCCESS with Session ID: " + String.valueOf(mySessionId) + " / " + String.valueOf(integer));
}
})
.addOnFailureListener(exception -> {
switch (((SplitInstallException) exception).getErrorCode()) {
case SplitInstallErrorCode.NETWORK_ERROR:
// Display a message that requests the user to establish a
// network connection.
showDownloadFailed();
break;
case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
checkForActiveDownloads();
break;
default:
debugLog("OnFailureListener > UNKNOWN ERROR : " + exception.getMessage());
}
});
addUpdateListener();
}
public static native void onMultilanguageDownloaded();
// =============== END OF SPLITTER ================
Код: Выделить всё
include ':feature_DE'
include ':feature_EN'
include ':feature_ES'
include ':feature_FR'
include ':feature_IN'
include ':feature_MS'
include ':feature_NL'
include ':feature_PT'
< /code>
build.gradledynamicFeatures = [
":feature_DE",
":feature_EN",
":feature_ES",
":feature_FR",
":feature_IN",
":feature_MS",
":feature_NL",
":feature_PT",
]
implementation "com.google.android.play:feature-delivery:2.1.0"
< /code>
Feature module manifest example:
< /code>
Question:
- Is there any special case or limitation for Play Feature Delivery on Android 10 and below?
- Has anyone else experienced modules not appearing in getInstalledModules() even though the status is INSTALLED?
- I implemented SplitInstallRequest and confirmed that the module download and installation process completes without errors.
- I added logging inside SplitInstallStateUpdatedListener to verify that the status changes to INSTALLED.
- I used splitInstallManager.getInstalledModules() in a loop (with delay) to check if the module name appears after installation.
- I also called SplitInstallHelper.updateAppInfo(context) and refreshed the asset manager after installation.
- After the status becomes INSTALLED, I expected splitInstallManager.getInstalledModules() to include the newly installed module (e.g. "feature_EN").
- I also expected to be able to load the module’s assets immediately (like it works on Android 11+).
Подробнее здесь: https://stackoverflow.com/questions/797 ... dmodules-o
Мобильная версия