Android 应用在移动数据和 wifi 上的工作方式不同

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

我有一个应用程序重复从 API 获取一些数据。当我有 wifi 连接时,这真的很好用。但是对于移动数据,我的请求要么超时,要么需要 20 秒。这没有意义,因为如果我只是在 android 浏览器中键入 API url,它几乎会立即加载。另外 API 是非 https api 我不知道这是否重要。知道是什么原因造成的吗? 我的 Ktor 客户:

object KtorClient {

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

}

我的产品库:

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() 

}

我的视图模型:

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)
        }
    }
}

}

浏览器向请求添加额外的 headers,DDOS 保护机制通常会根据各种标准(如客户端的 IP)延迟或简单地拒绝请求。有时,如果不在 header 中指定主机名,您甚至无法向服务器 IP 发出请求(可能是因为 IP 属于执行 DDOS 过滤的代理服务器,而不是终端服务器)。可能是代理不像 VPN 或宽带 ISP 那样信任移动运营商的 IP。您可以在设备上使用 packet/traffic 检查应用程序来分析浏览器发出的请求,并尝试在您自己的请求中复制它(复制 headers)。


server/cloud比webapp的实际代码更容易造成延迟。尝试使用不同的移动运营商,看看它们是否都会导致延迟。还要检查实际 IP 及其相关国家/地区。使用 traceroute 查看请求中的延迟可能会帮助您查明问题所在。

如果您使用的域不是原始 IP,您可能还想检查 DNS 解析是否导致延迟,尽管它不太可能导致显着和重复的延迟,因为它缓存了查询。

最后但并非最不重要的一点是检查您使用客户端的 HTTP 库,它可能会做一些奇怪的事情 'optimizations' 并请求排队,这可能会搞砸,尤其是如果您在应用程序处于 运行 时更改连接](众所周知,排球会在其默认设置中引起相当大的痛苦和缓存响应)。尝试使用原始 HttpUrlConnection 或 android 堆栈中最基本的任何内容,看看是否有任何改变。


奇怪的是 DNS 在第一次查询后不缓存 domain/IP 对,以便后续查询更快,可能是关于指示它不要缓存的域。据我所知,您无法通过蜂窝网络对 DNS 做太多事情,因为它是由运营商管理的,因此替代方法是在您的应用程序中定义和使用 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 可以使用)并使用已解析的 IP对于 session.

中的后续请求

你也可以看看finding current DNS in Android and sending a UDP DNS query