Android应用程序在Ktor无法连接到远程URL时崩溃。

huangapple go评论68阅读模式
英文:

Android app crashes when Ktor is unable to connect to remote URL

问题

你的Kotlin代码中的问题可能与Ktor的网络请求和错误处理有关。为了使应用程序在没有网络连接时不崩溃,你可以添加一些错误处理代码,例如使用try-catch块捕获网络请求的异常。

以下是一个示例,你可以在你的loadRemoteContent函数中添加错误处理:

private fun loadRemoteContent() {
    viewModelScope.launch {
        try {
            val lastUpdateCheck = preferencesRepository.readDate(Settings.LAST_UPDATE_CHECK_DATE)
            val lastUpdateCheckDate = LocalDate.ofEpochDay(lastUpdateCheck ?: today.toEpochDay())

            val period = Period.between(lastUpdateCheckDate, today)

            if (lastUpdateCheck == null || period.days >= 1) {
                val remoteHash = repository.getHash()
                val localHash = preferencesRepository.readString(Settings.LOCAL_HASH_STRING)

                if (remoteHash != localHash) {
                    val remoteContent = repository.getAllRemote()
                    repository.insertAllRemote(remoteContent)

                    preferencesRepository.setString(Settings.LOCAL_HASH_STRING, remoteHash)
                }

                preferencesRepository.setDate(Settings.LAST_UPDATE_CHECK_DATE, today.toEpochDay())
            }
        } catch (e: Exception) {
            // 处理网络请求异常,例如在Logcat中记录错误消息
            Log.e("NetworkError", "Error loading remote content: ${e.message}")
        }
    }
}

这将捕获在网络请求期间可能抛出的异常,从而防止应用程序崩溃。你可以根据需要进一步扩展错误处理以适应你的应用程序需求。

另外,请确保你的应用程序在AndroidManifest.xml中具有适当的Internet权限,以便访问网络。

希望这能帮助你解决问题!

英文:

The app I'm trying to build using Kotlin, Dagger-Hilt and Ktor has a ViewModel that periodically checks for new content on a remote server:

private fun loadRemoteContent() {
    viewModelScope.launch {
        val lastUpdateCheck = preferencesRepository.readDate(Settings.LAST_UPDATE_CHECK_DATE)
        val lastUpdateCheckDate = LocalDate.ofEpochDay(lastUpdateCheck ?: today.toEpochDay() )

        val period = Period.between(lastUpdateCheckDate, today)

        if (lastUpdateCheck == null || period.days >= 1) {
            val remoteHash = repository.getHash()
            val localHash = preferencesRepository.readString(Settings.LOCAL_HASH_STRING)

            if (remoteHash != localHash) {
                val remoteContent = repository.getAllRemote()
                repository.insertAllRemote(remoteContent)

                preferencesRepository.setString(Settings.LOCAL_HASH_STRING, remoteHash)
            }

            preferencesRepository.setDate(Settings.LAST_UPDATE_CHECK_DATE, today.toEpochDay())
        }
    }
}

The repository in turn has the following function:

suspend fun getAllRemote(): List<Item> = withContext(backgroundDispatcher) {
    ktorHttpClient
        .prepareGet("https://someurl.com/remotecontent.json")
        .execute { response: HttpResponse ->
            val remoteContent = response.body<List<ItemResponse>>()
            remoteContent.map { remote ->
                Item(
                    id = remote.id,
                    title = remote.title,
                    summary = remote.summary,
                    link = remote.link
                )
            }
        }
}

I have configured Ktor in the AppModule.kt file like this:

@Provides
@Singleton
fun provideKtorHttpClient(): HttpClient {
    return HttpClient(Android) {
        expectSuccess = true
        install(ContentNegotiation) {
            json(
                Json {
                    prettyPrint = true
                    isLenient = true
                    ignoreUnknownKeys = true
                }
            )
        }
        install(Logging) {
            logger = object : Logger {
                override fun log(message: String) {
                    Log.v("http log: ", message)
                }
            }
            level = LogLevel.ALL
        }

        install(DefaultRequest) {
            header(HttpHeaders.ContentType, ContentType.Application.Json)
        }

        install(HttpRequestRetry) {
            retryOnServerErrors(maxRetries = 5)
            exponentialDelay()
        }
    }
}

When run without an internet connection on an emulator inside Android Studio, Logcat shows the app trying to establish connection 5 times and then crashes with just this message:

2023-05-09 22:36:42.321 31747-31747 AndroidRuntime          com.example.myapp          E  FATAL EXCEPTION: main
                                                                                                    Process: com.example.myapp, PID: 31747

Could someone help me figure out what it is that I'm missing in the Ktor configuration or the functions, so that when there's no internet connection, the app may continue to run without crashing?

Thank you!

答案1

得分: 0

以下是翻译好的部分:

问题的关键,正如Aleksei Tirman建议的那样,在使用try {} catch {}块时在于以下方式:

suspend fun getAllRemote(): List<Item> = withContext(backgroundDispatcher) {
    try {
        ktorHttpClient
            .prepareGet("https://some-remote-api.xyz")
            .execute { response: HttpResponse ->
                val remoteItems = response.body<List<ItemResponse>>()
                remoteItems.map { remote ->
                    Item(
                        id = remote.id,
                        title = remote.title,
                        summary = remote.summary,
                        link = remote.link
                    )
                }
            }
    } catch (e: ServerResponseException) {
        Log.d("Ktor", "获取远程项目时出错:${e.response.status.description}")
        emptyList()
    } catch (e: ClientRequestException) {
        Log.d("Ktor", "获取远程项目时出错:${e.response.status.description}")
        emptyList()
    } catch (e: RedirectResponseException) {
        Log.d("Ktor", "获取远程项目时出错:${e.response.status.description}")
        emptyList()
    }
}
英文:

The key to the problem, as suggested by Aleksei Tirman, was in using a try {} catch {} block, like this:

suspend fun getAllRemote(): List<Item> = withContext(backgroundDispatcher) {
    try {
        ktorHttpClient
            .prepareGet("https://some-remote-api.xyz")
            .execute { response: HttpResponse ->
                val remoteItems = response.body<List<ItemResponse>>()
                remoteItems.map { remote ->
                    Item(
                        id = remote.id,
                        title = remote.title,
                        summary = remote.summary,
                        link = remote.link
                    )
                }
            }
    } catch (e: ServerResponseException) {
        Log.d("Ktor", "Error getting remote items: ${e.response.status.description}")
        emptyList()
    } catch (e: ClientRequestException) {
        Log.d("Ktor", "Error getting remote items: ${e.response.status.description}")
        emptyList()
    } catch (e: RedirectResponseException) {
        Log.d("Ktor", "Error getting remote items: ${e.response.status.description}")
        emptyList()
    }
}

huangapple
  • 本文由 发表于 2023年5月10日 16:07:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76216211.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定