Skip to content

Commit

Permalink
Merge pull request #42 from KazaKago/feature/fix_too_many_requests_ex…
Browse files Browse the repository at this point in the history
…ception

Fix TooManyRequestsException
  • Loading branch information
KazaKago authored Apr 14, 2023
2 parents 61250be + cec0472 commit 070645b
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 60 deletions.
2 changes: 1 addition & 1 deletion swr/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ android {
dependencies {
implementation(platform("androidx.compose:compose-bom:2023.04.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.lifecycle:lifecycle-common:2.6.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")

debugImplementation("androidx.compose.ui:ui-test-manifest")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.kazakago.swr.compose.internal

import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

internal class NetworkCallbackManager private constructor(
private val context: Context,
) {

companion object {
@SuppressLint("StaticFieldLeak")
private var instance: NetworkCallbackManager? = null

fun getInstance(context: Context): NetworkCallbackManager {
var instance = instance
return if (instance != null && instance.context == context.applicationContext) {
instance
} else {
instance = NetworkCallbackManager(context.applicationContext)
this.instance = instance
instance
}
}
}

private var isFirstTime = true
private val _onAvailable = MutableSharedFlow<Network>(extraBufferCapacity = 1)
val onAvailable = _onAvailable.asSharedFlow()

init {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
if (isFirstTime) {
isFirstTime = false
return
}
_onAvailable.tryEmit(network)
}
}
connectivityManager.registerNetworkCallback(request, networkCallback)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ package com.kazakago.swr.compose.internal

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.repeatOnLifecycle
import com.kazakago.swr.compose.cache.LocalSWRCache
import com.kazakago.swr.compose.cache.LocalSWRSystemCache
import com.kazakago.swr.compose.config.SWRConfig
Expand Down Expand Up @@ -62,31 +58,20 @@ internal fun <KEY, DATA> RevalidateOnFocus(
val systemCache = LocalSWRSystemCache.current
val revalidateOnFocus = config.revalidateOnFocus
val focusThrottleInterval = config.focusThrottleInterval
DisposableEffect(key, lifecycleOwner, systemCache, revalidateOnFocus, focusThrottleInterval) {
LaunchedEffect(key, lifecycleOwner, systemCache, revalidateOnFocus, focusThrottleInterval) {
if (revalidateOnFocus) {
var isFirstTime = true
val lifecycleObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
val focusedTimerJob = systemCache.getFocusedTimerJob(key)
if (!isFirstTime) {
if (focusedTimerJob == null || !focusedTimerJob.isActive) {
val newFocusedTimerJob = SWRGlobalScope.launch { delay(focusThrottleInterval) }
systemCache.setFocusedTimerJob(key, newFocusedTimerJob)
validate()
}
}
isFirstTime = false
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
val focusedTimerJob = systemCache.getFocusedTimerJob(key)
if (!isFirstTime) {
if (focusedTimerJob == null || !focusedTimerJob.isActive) {
val newFocusedTimerJob = SWRGlobalScope.launch { delay(focusThrottleInterval) }
systemCache.setFocusedTimerJob(key, newFocusedTimerJob)
validate()
}
else -> {}
}
isFirstTime = false
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
} else {
onDispose {}
}
}
}
Expand All @@ -100,42 +85,14 @@ internal fun <KEY, DATA> RevalidateOnReconnect(
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val revalidateOnReconnect = config.revalidateOnReconnect
DisposableEffect(key, lifecycleOwner, revalidateOnReconnect) {
LaunchedEffect(key, lifecycleOwner, revalidateOnReconnect) {
if (revalidateOnReconnect) {
var shouldNotValidateAtNextOnAvailable = false
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
if (!shouldNotValidateAtNextOnAvailable) {
validate()
}
shouldNotValidateAtNextOnAvailable = false
}
}
val lifecycleObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_START -> {
shouldNotValidateAtNextOnAvailable = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.let {
it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
} ?: false
connectivityManager.registerNetworkCallback(request, networkCallback)
}
Lifecycle.Event.ON_STOP -> {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
else -> {}
val networkCallbackManager = NetworkCallbackManager.getInstance(context)
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
networkCallbackManager.onAvailable.collect {
validate()
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
} else {
onDispose {}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,17 @@ public class RevalidateOnReconnectOptionTest {
}
}

val connectivityManager = shadowOf(composeTestRule.activity.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
connectivityManager.networkCallbacks.forEach { callback ->
callback.onAvailable(ShadowNetwork.newInstance(1)) // First registered time callback.
}

composeTestRule.mainClock.advanceTimeBy(2500)
stateList.map { it.data } shouldBe listOf(null, null, "fetched")
stateList.map { it.error } shouldBe listOf(null, null, null)
stateList.map { it.isLoading } shouldBe listOf(false, true, false)
stateList.map { it.isValidating } shouldBe listOf(false, true, false)

val connectivityManager = shadowOf(composeTestRule.activity.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
connectivityManager.networkCallbacks.forEach { callback ->
callback.onAvailable(ShadowNetwork.newInstance(1))
}
Expand Down

0 comments on commit 070645b

Please sign in to comment.