ql
KNET automatycznie kolejkuje requesty gdy urządzenie jest offline i wysyła je po powrocie połączenia.
import rip.nerd.kitsunenet.sync.KNETBackgroundSync
val sync = KNETBackgroundSync(context)
// Dodaj request do kolejki offline
sync.enqueue(
KNETRequest.post("https://api.example.com/orders", orderData)
)
// Request zostanie wysłany automatycznie
// gdy urządzenie będzie online
val sync = KNETBackgroundSync.Builder(context)
.setRetryPolicy(RetryPolicy.EXPONENTIAL)
.setMaxRetries(5)
.setRequiredNetworkType(NetworkType.CONNECTED) // lub UNMETERED
.setRequireCharging(false)
.setPersistQueue(true) // Przetrwa restart
.build()
// Start nasłuchiwania sieci
sync.start()
sync.enqueue(
request = KNETRequest.post(url, data),
priority = Priority.HIGH,
tag = "create-order",
deadline = System.currentTimeMillis() + 3600_000, // Max 1h
onSuccess = { response ->
showNotification("Zamówienie wysłane!")
},
onFailure = { error ->
showNotification("Błąd: ${error.message}")
}
)
// Sprawdź kolejkę
val pendingCount = sync.getPendingCount()
val pendingRequests = sync.getPendingRequests()
// Anuluj
sync.cancel(requestId)
sync.cancelByTag("create-order")
sync.cancelAll()
// Wymuś synchronizację
sync.syncNow()
// Status
val status = sync.getStatus()
// IDLE, SYNCING, WAITING_FOR_NETWORK, PAUSED
sync.addListener(object : SyncListener {
override fun onSyncStarted() {
showSyncIndicator()
}
override fun onSyncCompleted(successCount: Int, failedCount: Int) {
hideSyncIndicator()
showToast("Zsynchronizowano $successCount elementów")
}
override fun onRequestSent(request: KNETRequest, response: KNETResponse) {
Log.d("Sync", "Sent: ${request.url}")
}
override fun onRequestFailed(request: KNETRequest, error: Throwable) {
Log.e("Sync", "Failed: ${request.url} - ${error.message}")
}
override fun onNetworkAvailable() {
Log.d("Sync", "Network available, starting sync...")
}
override fun onNetworkLost() {
Log.d("Sync", "Network lost, queuing requests...")
}
})
sync.setConflictResolver { localRequest, serverResponse ->
// Obsłuż konflikt (np. 409 Conflict)
when {
serverResponse.statusCode == 409 -> {
// Pobierz aktualną wersję z serwera
val serverVersion = serverResponse.jsonObject()["version"] as Int
val localVersion = (localRequest.data as Map<*, *>)["version"] as Int
if (serverVersion > localVersion) {
ConflictResolution.USE_SERVER
} else {
ConflictResolution.RETRY_WITH_MERGE
}
}
else -> ConflictResolution.RETRY
}
}
// Alternatywnie - użyj interceptora
val interceptor = KNETOfflineInterceptor(
isOnline = { networkChecker.isConnected() },
offlineQueue = sync,
queueableMethod = setOf("POST", "PUT", "DELETE"),
fallbackToCache = true
)
val client = KNETClient.builder()
.addInterceptor(interceptor)
.build()
// Teraz POST/PUT/DELETE będą kolejkowane gdy offline
// Dla background sync użyj WorkManager
class SyncWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val sync = KNETBackgroundSync(applicationContext)
return try {
val result = sync.syncNow()
if (result.failedCount == 0) {
Result.success()
} else {
Result.retry()
}
} catch (e: Exception) {
Result.retry()
}
}
}
// Zaplanuj
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
15, TimeUnit.MINUTES
)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build()
WorkManager.getInstance(context).enqueue(syncRequest)
class OrderRepository(
private val client: KNETClient,
private val sync: KNETBackgroundSync,
private val localDb: OrderDatabase,
private val networkChecker: NetworkChecker
) {
suspend fun createOrder(order: Order): OrderResult {
// 1. Zapisz lokalnie
val localId = localDb.insertOrder(order.copy(
status = OrderStatus.PENDING_SYNC
))
// 2. Jeśli online - wyślij od razu
if (networkChecker.isOnline()) {
return try {
val response = client.post("$baseUrl/orders", order.toJson())
val serverOrder = response.json<Order>()
// Update local z server ID
localDb.updateOrder(localId, serverOrder)
OrderResult.Success(serverOrder)
} catch (e: Exception) {
// Fallback do queue
queueOrder(localId, order)
OrderResult.Queued(localId)
}
} else {
// 3. Offline - dodaj do kolejki
queueOrder(localId, order)
return OrderResult.Queued(localId)
}
}
private fun queueOrder(localId: Long, order: Order) {
sync.enqueue(
request = KNETRequest.post("$baseUrl/orders", order.toJson()),
tag = "order-$localId",
onSuccess = { response ->
val serverOrder = response.json<Order>()
localDb.updateOrder(localId, serverOrder)
}
)
}
}
sealed class OrderResult {
data class Success(val order: Order) : OrderResult()
data class Queued(val localId: Long) : OrderResult()
data class Error(val message: String) : OrderResult()
}