diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/ModelStore.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/ModelStore.kt index 5c9c7b6c67..91d4b41e05 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/ModelStore.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/ModelStore.kt @@ -154,31 +154,35 @@ abstract class ModelStore( } protected fun load() { + if (name == null || _prefs == null) { + return + } + + val str = _prefs.getString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + name, "[]") + val jsonArray = JSONArray(str) synchronized(models) { - if (name != null && _prefs != null) { - val str = _prefs.getString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + name, "[]") - val jsonArray = JSONArray(str) - for (index in 0 until jsonArray.length()) { - val newModel = create(jsonArray.getJSONObject(index)) ?: continue - models.add(newModel) - // listen for changes to this model - newModel.subscribe(this) - } + for (index in 0 until jsonArray.length()) { + val newModel = create(jsonArray.getJSONObject(index)) ?: continue + models.add(newModel) + // listen for changes to this model + newModel.subscribe(this) } } } fun persist() { - synchronized(models) { - if (name != null && _prefs != null) { - val jsonArray = JSONArray() - for (model in models) { - jsonArray.put(model.toJSON()) - } + if (name == null || _prefs == null) { + return + } - _prefs.saveString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + name, jsonArray.toString()) + val jsonArray = JSONArray() + synchronized(models) { + for (model in models) { + jsonArray.put(model.toJSON()) } } + + _prefs.saveString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + name, jsonArray.toString()) } override fun subscribe(handler: IModelStoreChangeHandler) = changeSubscription.subscribe(handler) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationModelStore.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationModelStore.kt index ec90751823..26eaf3ac32 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationModelStore.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationModelStore.kt @@ -28,7 +28,7 @@ import com.onesignal.user.internal.operations.impl.executors.UpdateUserOperation import org.json.JSONObject internal class OperationModelStore(prefs: IPreferencesService) : ModelStore("operations", prefs) { - init { + fun loadOperations() { load() } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt index ed3158845d..318e3f289b 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt @@ -77,10 +77,6 @@ internal class OperationRepo( } } this.executorsMap = executorsMap - - for (operation in _operationModelStore.list()) { - internalEnqueue(OperationQueueItem(operation, bucket = enqueueIntoBucket), flush = false, addToStore = false) - } } override fun containsInstanceOf(type: KClass): Boolean { @@ -91,7 +87,11 @@ internal class OperationRepo( override fun start() { paused = false - coroutineScope.launch { processQueueForever() } + coroutineScope.launch { + // load saved operations first then start processing the queue to ensure correct operation order + loadSavedOperations() + processQueueForever() + } } override fun enqueue( @@ -121,13 +121,18 @@ internal class OperationRepo( queueItem: OperationQueueItem, flush: Boolean, addToStore: Boolean, + index: Int? = null, ) { synchronized(queue) { - queue.add(queueItem) - if (addToStore) { - _operationModelStore.add(queueItem.operation) + if (index != null) { + queue.add(index, queueItem) + } else { + queue.add(queueItem) } } + if (addToStore) { + _operationModelStore.add(queueItem.operation) + } waiter.wake(LoopWaiterMessage(flush, 0)) } @@ -332,12 +337,20 @@ internal class OperationRepo( } val startingKey = - if (startingOp.operation.groupComparisonType == GroupComparisonType.CREATE) startingOp.operation.createComparisonKey else startingOp.operation.modifyComparisonKey + if (startingOp.operation.groupComparisonType == GroupComparisonType.CREATE) { + startingOp.operation.createComparisonKey + } else { + startingOp.operation.modifyComparisonKey + } if (queue.isNotEmpty()) { for (item in queue.toList()) { val itemKey = - if (startingOp.operation.groupComparisonType == GroupComparisonType.CREATE) item.operation.createComparisonKey else item.operation.modifyComparisonKey + if (startingOp.operation.groupComparisonType == GroupComparisonType.CREATE) { + item.operation.createComparisonKey + } else { + item.operation.modifyComparisonKey + } if (itemKey == "" && startingKey == "") { throw Exception("Both comparison keys can not be blank!") @@ -352,4 +365,21 @@ internal class OperationRepo( return ops } + + /** + * Load saved operations from preference service and add them into the queue + * NOTE: Sometimes the loading might take longer than expected due to I/O reads from disk + * Any I/O implies executing time will vary greatly. + */ + private fun loadSavedOperations() { + _operationModelStore.loadOperations() + for (operation in _operationModelStore.list().withIndex()) { + internalEnqueue( + OperationQueueItem(operation.value, bucket = enqueueIntoBucket), + flush = false, + addToStore = false, + operation.index, + ) + } + } } diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt index f79d7a1a01..e3ff258b8d 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt @@ -35,6 +35,7 @@ private class Mocks { val operationModelStore: OperationModelStore = run { val mockOperationModelStore = mockk() + every { mockOperationModelStore.loadOperations() } just runs every { mockOperationModelStore.list() } returns listOf() every { mockOperationModelStore.add(any()) } just runs every { mockOperationModelStore.remove(any()) } just runs