I am fairly new at working with API calls in Android Studio and I'm having trouble with a block of code. It's a function that deletes a specific task item from a remote database.
Here's my code.
RepoInterface:
suspend fun deleteRemoteTask(token: String, id: Int, task: Task): Response<Task>
RepoImplementation:
override suspend fun deleteRemoteTask(token: String, id: Int, task : Task): Response<Task> = withContext(Dispatchers.IO) {
return#withContext try {
apiService.deleteTask(token, id, task)
} catch (e: Exception) {
Log.d("TAG", "deleteRemoteTask: Api Failed to run")
throw Exception(e.localizedMessage)
}
}
Repository:
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful")
}
}
I'm confident that my interface and implementation are set up correctly, but the logic in my actual repository is a bit shaky, as I keep getting an Exception thrown. The data is coming down from the View -> Viewmodel -> UseCase -> Repo -> RepoImp -> Interface -> API.
I also have
We would need a bit more info. The flow is not well implemented but no worries we all start by a point :D, I will adapt my answer to this flow.
Let's start with:
avoiding the !! non null operator in the response.body()!!.
If you mark that the response body is not null, in case it is it will crash, local.deleteTask should receive a body response nullable parameter and should handle the possible null body.
First the first; let's wrap the complete function in Repository into a try catch, something like:
try {
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful")
}
} catch(e: Exception) {
throw e
}
Let's check why that response is not successful, I see you throwing an Exception with a message in case the response is not successful, try to throw the Exception with the response.code(), something like:
try {
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful, response error code: ${response.code()}")
}
} catch(e: Exception) {
throw e
}
In your RepoImplementation, let's print that Exception stack trace and log it:
override suspend fun deleteRemoteTask(token: String, id: Int, task : Task): Response<Task> = withContext(Dispatchers.IO) {
return#withContext try {
apiService.deleteTask(token, id, task)
} catch (e: Exception) {
Log.d("TAG", "deleteRemoteTask: Api Failed to run, stack trace: ${e.printStackTrace()}")
}
}
Then in the logcat you can search for the TAG and see what is going on with that response. Let me know how it goes :D
Related
I am trying to make sure my app responds appropriate in the event of a backend failure, I am using realm/mongo to create an async task that fetches the user.
I have these two blocks:
override suspend fun logIn(accessToken: String) {
val user = logInInternal(accessToken)
realmAsyncOpen(user)
}
and
private suspend fun logInInternal(accessToken: String) = suspendCancellableCoroutine<User> { continuation ->
val customJWTCredentials: Credentials = Credentials.jwt(accessToken)
app.loginAsync(customJWTCredentials) {
if (it.isSuccess) {
continuation.resumeWith(Result.success(app.currentUser()!!))
} else {
continuation.resumeWithException(RealmLoginException().initCause(it.error))
}
}
}
logInInternal crashes when I hit the resumeWithException part. I have also tried using app.login(credentials) since the method is suspending, without luck there. Why does my app crash when I resume with exception?
I am causing the call to 502 out when hit.
The docs of resumeWithException say:
Resumes the execution of the corresponding coroutine so that the exception is re-thrown right after the last suspension point.
Which means you need to catch that exception:
override suspend fun logIn(accessToken: String) {
try {
val user = logInInternal(accessToken)
realmAsyncOpen(user)
} catch(e: RealmLoginException /*or e: Exception - to catch all exceptions*/) {
// handle exception
}
}
Background: I am fetching data from Bluetooth and after every packet is received it is processed. What I am trying to do is to start timeout when data processing finishes and stop the timer when a new packet is received.
Tried creating a timeout logic using Flow. I created a short snippet to test if it works:
class ExceptionPropagationTest {
#Test
fun test()= runBlocking {
println(get(coroutineContext))
}
suspend fun get(coroutineContext: CoroutineContext) = withContext(coroutineContext) {
try {
enableDataTransferTimeout()
delay(3000)
"Result"
} catch (e: IllegalStateException) {
println("Exception caught ${System.currentTimeMillis()}")
"No Result"
}
}
private fun CoroutineScope.enableDataTransferTimeout() {
flowOf("1").onEach {
delay(500)
doSomething()
throw IllegalStateException()
}.launchIn(this)
}
private suspend fun doSomething(){
// Do some suspending work
}
}
Above code first prints:
Exception caught [CURRENT_TIME]
Then logs exceptions stack trace and crashes:
java.lang.IllegalStateException at
com.app.ExceptionPropagationTest$enableDataTransferTimeout$1.invokeSuspend(ExceptionPropagationTest.kt:49)
(Coroutine boundary) at
com.app.ExceptionPropagationTest$test$1.invokeSuspend(ExceptionPropagationTest.kt:32)
Caused by: java.lang.IllegalStateException
Question: Is there any way to catch the exception and return value without propagating the exception to parent scope?
If it is not possible with flow any other solution or suggestion is welcome.
You can use the catch method. Docs here
flowOf("1")
.map {
delay(500)
doSomething()
throw IllegalStateException()
}
.catch { ... } // catches exceptions in map or other operands you applied
.collect()
I need to implement a custom gRPC on Kotlin native side.
#ReactMethod can't be suspend func.
How can I run it?
#ReactMethod
fun connect(ipAddress: String, port: Int) {
try {
channel = ManagedChannelBuilder.forAddress(ipAddress, port).usePlaintext().build()
var guidKey = Metadata.Key.of("GUID", Metadata.ASCII_STRING_MARSHALLER)
metadata.put(guidKey, GUID)
val stub = DBServiceGrpcKt.DBServiceCoroutineStub(channel!!)
var request = GrpcDBService.SignInRequest.newBuilder()
.setUserName("user")
.setPassword("11111")
.build()
try {
//******* this part *****
suspend fun coroutine() {
var response = stub.trySignIn(request,metadata)
}
} catch (e: Exception) {
Log.d("grpcConnect", e.localizedMessage)
}
} catch (e: Error) {
Log.d("grpcConnect ", e.localizedMessage)
}
finally {
channel?.shutdown()
}
}
You need to create a coroutine, for example, define a scope (somewhere in your class) and use it with launch:
myPluginScope.launch {
val response = stub.trySignIn(request,metadata)
// Return, the result
}
Creating the scope is easy, the tricky part is to find where to cancel it. Check the documentation on React native modules to find a good place to call cancel on your scope:
val myPluginScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
Now, is important to decide whether the connect function should behave as an asynchronous function or not. For example, if the sign in operation takes time or uses the network, connect should probably return the result through a Promise or Callback, so the Javascript side is not blocked:
#ReactMethod
fun connect(ipAddress: String, port: Int, promise: Promise) {
myPluginScope.launch {
try {
// Make the sign in happen in a separate thread:
val response = withContext(context = Dispatchers.IO) {
stub.trySignIn(request, metadata)
}
// Resolve the promise in the calling thread (The UI thread)
promise.resolve(response.hypotheticalCode)
} catch (e: Exception) {
promise.reject("Sign in error!", e)
}
}
}
See:
https://reactnative.dev/docs/native-modules-android#promises
https://kotlinlang.org/docs/async-programming.html#coroutines
How to determine which exception is thrown and get the status code out of it in Spring Webflux.
This is the structure of my controller code.
#GetMapping("/")
fun getResults() : Mono<ResponseEntity<AccountDTO>>{
return Service.getResult()
.map {
}.doOnError {
//how to get statuscode here
throw ResponseStatusException(HttpStatus.NOT_FOUND, it.message!!)
}
Here I can get the custom message thrown, but how to get the status code? Instead of HttpStatus.NOT_FOUND. I want to capture the status code that is thrown by the service layer. Or is there a way to get the exception thrown?
I found a solution that works.
#GetMapping("/")
fun getResults() : Mono<ResponseEntity<AccountDTO>>{
return Service.getResult()
.map {
}.doOnError {
if(it is NotFoundException)
{
throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
else{
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR)
}
}
According to the source of Closable.use, if an error occurs, an exception will be thrown.
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
In most examples of Closable.use, try-catch is not used as shown below.
Why isn't error handling needed? Is it safe?
BufferedReader(FileReader("test.file")).use { return it.readLine() }
This line
BufferedReader(FileReader("test.file")).use { return it.readLine() }
is not safe. Reading and closing the reader can both throw IOExceptions, which are not RuntimeExceptions (caused by programming errors). That means leaving them uncaught exposes your app to crashing from things outside your control.
Since Kotlin doesn't have checked exceptions, the compiler won't warn you about this. To do this safely, you need to wrap it in try/catch. And if you want to handle read errors differently than close errors, you either need to have inner and outer try/catch statements:
try {
BufferedReader(FileReader("test.file")).use {
try {
return it.readLine()
catch (e: IOException) {
println("Failed to read line")
}
}
} catch (e: IOException) {
println("Failed to close reader")
}
or wrap the whole thing and extract any suppressed exceptions, but then its cumbersome to distinguish between them:
try {
BufferedReader(FileReader("test.file")).use { return it.readLine() }
} catch (e: IOException) {
val throwables = listOf(e, *e.suppressed)
for (throwable in throwables)
println(throwable.message)
}
But in practice, you're probably not going to react differently to various IOExceptions, so you can just put the one try/catch outside.
We see from Kotlin documentation what is the purpose of the use function:
Executes the given block function on this resource and then closes it
down correctly whether an exception is thrown or not.
This function closes the resource properly if the block function completed successfully or threw an exception. It is your responsibility to handle the result of the block function.
If an exception was thrown and there is a way to handle it and proceed with code execution, use a try/catch. If there is nothing to do about it and control should be passed to the caller, it is not necessary to use a try/catch.