英文:
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()
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论