Try block throws exception, but catch block does not catch it - android

I am using google login and I'm getting an exception. However, it looks like I'm unable to catch it.
I have the following code:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RC_SIGN_IN -> {
val task = try{
GoogleSignIn.getSignedInAccountFromIntent(data)
} catch (e: ApiException) {
Log.d("EXCEPTION", e.message)
when(e.statusCode) {
12501 -> return
else -> {
e.printStackTrace()
return
}
}
}
...
From Logcat I can see, that ApiException is the right Exception to catch:
And its definetly the one I am catching:
At first I thought the direct assignment with val task = try... might be responsible for this for some reason, so I changed my code accordingly and tried this:
var task: Task<GoogleSignInAccount>?
try{
task = GoogleSignIn.getSignedInAccountFromIntent(data)
} catch (e: ApiException) {
Log.d("EXCEPTION", e.message)
when(e.statusCode) {
12501 -> task = null
else -> {
e.printStackTrace()
task = null
}
}
}
Still, same behavior. So I thought maybe there is another exception being thrown, so I added another catch block:
var task: Task<GoogleSignInAccount>?
try{
task = GoogleSignIn.getSignedInAccountFromIntent(data)
} catch (e: ApiException) {
Log.d("EXCEPTION", e.message)
when(e.statusCode) {
12501 -> task = null
else -> {
e.printStackTrace()
task = null
}
}
} catch (e: Exception) {
Log.d("EXCEPTION", e.message)
task = null
}
Still the same behavior. I have spotted this code with breakpoints on every step but it just ignores the catch block completely. I stop at the try block:
But if I resume, the next step is outside of my try-catch block.
Can someone explain that behavior?

Just as I asked this question, I realized something:
The catch block is ignored because the try block doesn't really throw an exception.
The task object is set and its the one that holds the exception. When the task object is used later like this: val account = task.getResult(ApiException::class.java) then that's where the exception is actually thrown. At least that's what I expect here. The exception itself is basically caught, kept and thrown at a later point in time, but with the old stacktrace...
Not 100% sure about the inner workings of this, but that's what I could tell from logcat and my code.
So, instead of a try-catch, I simply went and handled the exception manually:
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
if(task.exception != null) {
val e = task.exception as? ApiException
if(e != null) {
when(e.statusCode) {
12501 -> return
else -> {
e.printStackTrace()
return
}
}
} else {
task.exception?.printStackTrace()
}
return
}

Related

ktor client: cant catch ConnectException even when I catch all Exceptions and Throwables

I'm using ktor client to make a simple REST request, when a network exception error occurs inside the client.get() call, I cannot catch this error no matter what I do and it causes the app to crash.
Keep in mind, I'm NOT trying to find the cause/solution of the crash, I'm trying to figure out how I can catch the Exception so the app doesnt crash.
here are all things I've tried:
try {
val response = runCatching {
client.post("$BASE_URL/api/users/login")
}.getOrElse {
throw it
}
return Result.Success(response.body())
} catch (e: RedirectResponseException) { // 3xx
Result.Error(e, e.response.status.description)
} catch (e: ClientRequestException) { // 4xx
Result.Error(e, e.response.status.description)
} catch (e: ServerResponseException) { // 5xx
Result.Error(e, e.response.status.description)
} catch (e: java.net.ConnectException) {
Result.Error(e, e.message.orEmpty())
} catch (e: Exception) {
Result.Error(e, e.message.orEmpty())
} catch (e: Throwable) {
Result.Error(java.lang.Exception(), e.message.orEmpty())
}
None of these is able to catch the crash.
I'm assuming the crash is happening on another thread or process and that's why I cant catch it, and that I need to add some sort of error handler to the httpClient during initialization, However I cant figure out how to do this. This is what I have so far in my httpClient Initialization:
val client = HttpClient(Android) {
install(Logging) {
level = LogLevel.ALL
}
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
}
The actauly exception that's causing my app to crash is this:
java.net.ConnectException: Failed to connect to localhost/127.0.0.1:8090
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)

Android studio kotlin function throw exeption

Im developing an app using kotlin and MVVM architecture.I have the three layers activity,viewModel and repository, in repository i have renameDirectory() function it does some network calling to rename a directory, the function can throw an exception if the network response returns an error the problem is that the catch block in the activity layer does not catch the exception.
renameDirectory in repository
suspend fun renameDirectory(token : String,directory: Directory) {
val resp = maApi.renameDirectory("jwt $token",directory)
if(resp.isSuccessful)
return
val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
val errorResponse =
gson.fromJson<ErrorResponse>(resp.errorBody()!!.charStream(), type)
throw Exception(errorResponse.error)
}
code in viewModel that calls the function
suspend fun renameDirectory(directory: Directory){
viewModelScope.launch (Dispatchers.IO){
directoriesRepository.renameDirectory(userResp!!.token!!,directory)
}
}
code in activity to calls the function and handle exceptions
try {
viewModel.renameDirectory(directory)
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar("Directory has been renamed successfully")
currentFragment.clearSelection()
}
} catch (ex: IOException) {
Log.d("IO Exception=>", ex.toString())
} catch (ex: HttpException) {
Log.d("Http Exception=>", ex.message())
} catch (ex: Exception) {
this.cancel()
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar(ex.message!!)
}
}
when renameDirectory in repository calls throw Exception() the app stops,why the code in activity does not handle the exception?

'if' must have both main and 'else' branches if used as an expression

I have the code below working before, now compiler halts and marks both if statements and says:
'if' must have both main and 'else' branches if used as an expression
But as you see this is not an expression, but just a simple equality statement and a conditional statement next to it.
try {
val json_string = responseBody!!.string()
val jsonObject = JSONObject(json_string)
if (jsonObject.has("version")) {
val remoteVersion = jsonObject.getInt("version")
if (remoteVersion > BuildConfig.VERSION_CODE) {
handler.post {
showInstallNewVersionDialog()
}
}
}
} catch (e: Exception) {
e.message?.let { Log.e(Constants.TAG, e.message!!) }
}
The funny part is if I added empty else tags, it will run but will warn to remove empty else statements:
if (jsonObject.has("version")) {
val remoteVersion = jsonObject.getInt("version")
if (remoteVersion > BuildConfig.VERSION_CODE) {
handler.post {
showInstallNewVersionDialog()
}
} else {}
} else {}
If IDE tells you that 'if' must have both main and 'else' branches if used as an expression then it is so. Most likely this try-catch construction is defined as a custom getter of a variable or as single-expression function.
An example:
val aVariable =
try {
if (System.currentTimeMillis() == 0L) {
println("It is 1970-01-01")
}
} catch (e: Exception) {
// empty
}
fun aFunction() =
try {
if (System.currentTimeMillis() == 0L) {
println("It is 1970-01-01")
}
} catch (e: Exception) {
// empty
}
And the IDE (lint) shows me an error even before compiling. Same error for the function.
To fix this issue you either introduce else statement OR redeclare this variable as a function (or update the function you have). This function will work fine as it always returns Unit even if you do not have any code in it.
fun aFunction() {
try {
if (System.currentTimeMillis() == 0L) {
println("It is 1970-01-01")
}
} catch (e: Exception) {
// empty
}
}
When you use single-expression functions or getters you MUST return a value. That is why else part is required.
The issue is that Kotlin sees try {} catch {} construct as a "function" that returns a value, which is not the same as Java try {} catch {}.
As a result compiler assumes that you "forgot" to code the else branches even if it is clearly the case of you not wanting to return any value.
Kotlin sometimes is ludicrous.
i.e. you can write:
val t = try {
"test"
} catch (e: Exception) {
"error"
}
println(t)
As noted above, "Kotlin sometimes is ludicrous". In my case I was trying to do something in the catch statement:
fun x() {
try {
doSomething()
doSomethingIfNoException()
} catch (e:Exception) {
if(c) doSomethingForException()
else ""
}
}
Kotlin insisted on the else in the catch. Putting the else"" was the easiest fix I could find.

Kotlin coroutines resumeWithException error

I decided to wrap getting device location (once, without updating) using kotlin coriutines, so finally i got this code:
#SuppressLint("MissingPermission")
suspend fun LocationManager.getCurrentLocationOnce(): Location {
return suspendCancellableCoroutine { continuation ->
try {
val locationListener = object : SimpleLocationListener {
override fun onLocationChanged(location: Location?) {
if (location == null) {
this#getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(FailedToRetrieveLocationException("Location is NULL"))
} else {
this#getCurrentLocationOnce.removeUpdates(this)
continuation.resume(location)
}
}
override fun onProviderEnabled(provider: String?) {}
override fun onProviderDisabled(provider: String?) {
this#getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
}
this.requestSingleUpdate(
LocationManager.GPS_PROVIDER,
locationListener,
null
)
} catch (e : Exception) {
continuation.resumeWithException(e)
}
}
}
When GPS is ON all works fine, but when GPS is OFF program fails with exception ProviderDisabledException, thats because of:
override fun onProviderDisabled(provider: String?) {
this#getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
But i don't know why it's fails, because in place where i'm using this function i've got:
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
So i'm logging exception and rethrow it to caller function and in caller function i'm catching this exception:
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) {
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
And in error stacktrace i've got only this:
E/[GOOGLE] downloadRestaurantsWithGPSLocationGoogle: my.package.location.exceptions.ProviderDisabledException: Provider gps disabled
at my.package.common.location.LocationUtilsKt$getCurrentLocationOnce$$inlined$suspendCancellableCoroutine$lambda$1.onProviderDisabled(LocationUtils.kt:45)
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:384)
at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:300)
at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:316)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:6878)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
I don't know why app fails, because code like this works perfect:
lifecycleScope.launch {
try {
throwError()
} catch (e : Exception) {
e.printStackTrace()
}
}
private suspend fun throwError() {
return suspendCancellableCoroutine { continuation ->
continuation.resumeWithException(ProviderDisabledException("TEST"))
}
}
So, finally i realized why it's crash the app =). All ok with coroutines.
Problem is in this method:
#Throws(ProviderDisabledException::class, FailedToRetrieveLocationException::class)
private fun downloadRestaurantsWithGPSLocationGoogle() = lifecycleScope.launch {
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", "Trying to get location via GPS")
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
ex.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
providerException.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
e.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
}
And the problem is that i'm throwing exception from coroutine and handle this exception not in coroutine, so i'm launched my coroutine and all try-cathces are skipped, because here i'm using fire and forget style. So to fix this i need to do this method suspend and throw exceptions.
Place where trying to catch errors:
private fun downloadRestaurants() = lifecycleScope.launch {
log("downloadRestaurantsWithLocationSort",
"Requesting Manifest.permission.ACCESS_COARSE_LOCATION & Manifest.permission.ACCESS_FINE_LOCATION permissions")
val user = requestPermissions(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (!user.any { !it.second }) {
// permission is granted, can download restaurants and sort by nearest
log("downloadRestaurantsWithLocationSort", "Permissions is granted")
log("MANUFACTURER", Build.MANUFACTURER)
if (Build.MANUFACTURER == "Huawei" || Build.MANUFACTURER == "HUAWEI") {
showToast("HUAWEI")
try {
log("[HUAWEI] downloadRestaurants", "Starting donwload restaurants for HUAWEI")
downloadRestaurantsWithGPSLocationHuawei()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[HUAWEI] error happened while getting location", e)
mainViewModel.downloadRestaurantsHeaders(null)
}
} else {
showToast("NOT A HUAWEI")
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
}
} else {
// permission is not granted, just download the restaurants
log("downloadRestaurantsWithLocationSort", "Permissions is NOT granted")
mainViewModel.downloadRestaurantsHeaders(null)
}
}
So the answer make functions downloadRestaurantsWithGPSLocationGoogle and downloadRestaurantsWithFusedLocationGoogle suspend and don't launch separate coroutine inside them. (remove lifecycleScope.launch)

how can i wrap this (AdsWizz) Kotlin callback function in a couroutine?

I'm new to coroutines and having a hard time figuring out how to correctly wrap an existing callback in a coroutine.
My goal is to be able to do the following:
lifecycleScope.launch {
withContext(Dispatchers.Main) {
val theResult = getPreRollAd() //1. call this suspending func and wait for result
doSomethingWithResult(theResult) //2. now that the result is returned from AdsWizz API (below), do something with it
}
}
Here is the AdsWizz API call that I'd like to "wrap":
val adReqInterface: AdRequestHandlerInterface = object :
AdRequestHandlerInterface {
override fun onResponseError(error: AdswizzSDKError) {
Timber.e("onResponseError $error")
}
override fun onResponseReady(adResponse: AdResponse) {
Timber.d( "onResponseReadySingleAd")
//this contains the url to the ad, title, etc..
!!!*** I WANT TO RETURN THE adResponse.mediaFile?.source string back to "theResult" variable above (in lifecycleScope.launch {.... )
}
}
try {
AdswizzSDK.getAdsLoader().requestAd(adReqParams, adReqInterface)
} catch (e: IllegalArgumentException) {
Timber.d( "IllegalArgumentException")
} catch (e: SecurityException) {
Timber.d( "SecurityException")
} catch (e: Exception) {
Timber.d( "other exception")
e.printStackTrace()
}
I've tried using suspendCoroutine {... to wrap but nothing is working. Really appreciate someones help re the right way to achieve this.
the right way to do it is to use suspendCancellableCoroutine. It can return a result or can be cancelled with an exception.
suspend fun getPreRollAd(): AdResponse {
return suspendCancellableCoroutine {
...
val adReqInterface: AdRequestHandlerInterface = object : AdRequestHandlerInterface {
override fun onResponseError(error: AdswizzSDKError) {
Timber.e("onResponseError $error")
it.cancel(error)
}
override fun onResponseReady(adResponse: AdResponse) {
Timber.d( "onResponseReadySingleAd")
it.resume(adResponse)
}
}
AdswizzSDK.getAdsLoader().requestAd(adReqParams, adReqInterface)
}
}
viewModelScope.launch {
val result = try {
getPreRollAd()
} catch(e: Throwable) {
null
}
...
}

Categories

Resources