'Android app doesn't work the same with mobile data as with wifi

I have an App which repeatedly gets some data from an API. That works really good when I have a wifi connection. But with mobile data my request either times out or takes like 20 seconds. Which doesn't make sense because if I just type the API url in the android browser it almost immediately loads up. Also the API is a non https api I don't know if that matters. Any idea what could cause this? My Ktor Client:

object KtorClient {

val ktorClient = HttpClient(Android) {
    install(DefaultRequest) {
        header("apiKey", "apiKey")
    }
    install(ContentNegotiation) {
        json()
    }
    defaultRequest {
        contentType(ContentType.Application.Json)
        accept(ContentType.Application.Json)
    }
}

}

My product repository:

object ProductRepository {

const val URL = "API"
const val LATEST_APK_URI = "$URL/apk"

suspend fun getProducts() = ktorClient.get("$URL/products").body<Map<String, List<ProductItem>>>().map {
    it.key to it.value.sortedBy { item -> item.done }
}.toMap() 

}

My viewmodel:

class ProductViewModel : ViewModel() {

val productFlow = mutableStateMapOf<String, List<ProductItem>>()
val errorFlow = MutableStateFlow<Error?>(null)
val lastRefreshFlow = MutableStateFlow<DateTimeTz?>(null)

fun refreshProducts(context: Context) {
    viewModelScope.launch {
        kotlin.runCatching {
            ProductRepository.getProducts()
        }.onSuccess {
            productFlow.clear()
            productFlow.putAll(it)
            lastRefreshFlow.value = DateTime.nowLocal()
            //updateOrder(context)
            context.cacheFile.updateCache(productFlow)
        }.onFailure {
            errorFlow.value = SilentError("Konnte Produkte nicht neu laden", it)
        }
    }
}

}


Solution 1:[1]

The browser adds extra headers to the request which are used by DDOS protection mechanisms which usually delay or simply reject the request depending on various criteria like the client's IP. Sometimes you can't even make the request to a server IP without specifying the hostname in a header (presumably because the IP is of a proxy server doing the DDOS filtering and not the end server). It might be that the proxy doesn't trust the mobile carrier's IPs as much as the ones of the VPNs or broadband ISPs. You may use a packet/traffic inspection app on the device to analyse the request made by the browser and try to replicate it (copy the headers) in your own requests.


The server/cloud is more likely to cause the delay than the actual code of the webapp. Try using different mobile operators to see if they all cause the delay. Also check the actual IPs and their associated country. Using traceroute to see the delays in your request might help you pinpointing the problem.

If you're using a domain not a raw IP you might also want to check if the DNS resolution causes the delay although it's unlikely to cause significant and repeated delays because it caches queries.

And last but not least check the HTTP library you use client side, it might do some weird 'optimizations' and request queuing which could get messed up especially if you change connection while the app is running (volley is known to cause quite a grief and cache responses in its default setup). Try using a raw HttpUrlConnection or whatever is most basic in your android stack to see if that changes anything.


It's weird the DNS doesn't cache the domain/IP pair after the first query so that subsequent queries are faster, probably something about the domain that instructs it not to. As far as I know you can't do much about the DNS over cellular, as it's managed by the carrier so the alternative would be to define and use a VPN with its own DNS. If that seems quite over the top or inconvenient you could also try to make a direct DNS query (or check if this Java library can be used) in your app and use the resolved IP for subsequent requests in that session.

You can also have a look at finding current DNS in Android and sending a UDP DNS query

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1