https://kotlinlang.org logo
#android
Title
# android
s

Slackbot

03/16/2020, 5:49 PM
This message was deleted.
s

Steve

03/16/2020, 5:53 PM
Does the Exception you're printing out say anything?
s

Shawn Karber_

03/16/2020, 5:59 PM
no, its not even getting that so far as I can tell
I think its not hitting the catch block, its instead returning something else from the try block, because the debugger in android studio just returns this error:
Copy code
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.karbon.shfthitapidemo, PID: 12058
which is absolutely unhelpful
s

Sean Keane

03/16/2020, 6:16 PM
That is actually really helpful. This is because you are launching this from a coroutine without context or scope to the main thread. So its trying to directly call back to it.
Can you show me the snipped of code where you call this?
s

Shawn Karber_

03/16/2020, 6:57 PM
I think figured out whats going on, its not even hitting the catch, its just returning a bad response message. Now I am using this code:
Copy code
suspend fun hitAPI(uri: String): String {
    val client = HttpClient()
    return try {
        val response: HttpResponse = client.get(uri)
        if (response.status.value == 200) {
            "${response.content}"
        } else { "There was error attempting to reach $uri"}
    } catch (e: Exception) {
        "Error fetching $uri."
    } finally {
        client.close()
    }
}
and its catching the error, but now the content is just displaying as:
Copy code
<http://io.ktor.utils.io|io.ktor.utils.io>.ByteBufferChannel@<mem-address>
s

Sean Keane

03/16/2020, 6:58 PM
Ok, where are you calling this code from? I want to see where your calling the suspended function from for the CoroutineScope
s

Shawn Karber_

03/16/2020, 6:59 PM
its tiny poc im using to test the http stuff out for a bigger project:
Copy code
package com.karbon.shfthitapidemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import com.karbon.shiftHDsdk.makeHttpRequest

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btn = findViewById<Button>(R.id.http_button)
        btn.setOnClickListener {
            makeCall()
        }
    }

    fun makeCall(): Unit {
        findViewById<TextView>(R.id.txt_v).text = makeHttpRequest("<https://www.google.com>")
    }

}
Copy code
import android.os.Build
import android.util.Log
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import java.lang.reflect.Field

import java.text.SimpleDateFormat
import java.util.Date

/*
 *  Gathers the environment data for the OS version, device identifiers, and the version of the
 *  application implementing this SDK. Useful for debugging and logging of developer, tester,
 *  and patient devices or environments. It uses reflection of the `Build.VERSION_CODES` enum
 *  to discern the name of the SDK given its SDK_INT.
 *
 * @return: String
 *   a string representation of the android environment at runtime including:
 *     - the device manufacturer, and model name
 *     - the user-readable SDK name (i.e. Marshmallow, KitKat, etc.)
 *     - the SDK Int representing the current SDK (i.e. 29 {'Q'})
 *     - the security patch identifier
 *     - the current datetime
 */
actual fun environmentMetaData(): String {
    val manMod = "Manufacturer/Model: ${Build.MANUFACTURER} ${Build.MODEL}"
    val fields: Array<Field> = Build.VERSION_CODES().javaClass.declaredFields
    val sdkId = Build.VERSION.SDK_INT
    val sdkName = fields[sdkId].name
    val osVersion = "OS: $sdkName ${sdkId}.${Build.VERSION.RELEASE}.${Build.VERSION.INCREMENTAL}"
    val secPatch = "Security Patch: ${Build.VERSION.SECURITY_PATCH}"
    val datetime = "DateTime: ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(Date())}"

    return "$manMod\n$osVersion\n$secPatch\n$datetime"
}

/*
 * Makes an HTTP get request using kotlinx coroutines (runBlocking and async/await) to the
 * specified uri.
 *
 * @params
 *   - uri: String - the url receiving the HTTP request
 *
 * @return: String
 *   a string representation of the HTTP get request
 *     - the string representation of the results if successful, error message otherwise
 */
actual fun httpRequest(uri: String): String {
    return fetcher(uri)
}

/*
 * A coroutine function that can block while waiting for the HTTP request to finish.
 *
 * @params
 *   - uri: String - the url receiving the HTTP request
 *
 * @return: String
 *   a string representation of the HTTP get request
 *     - the string representation of the results if successful, error message otherwise
 */
private fun fetcher(uri: String) = runBlocking<String> {
    val result = async {
        hitAPI(uri)
    }
    result.await()
}

/*
 * The function making the HTTP call using ktor functions. It is returns a result string from the
 * try block if successful or an error message from the catch block, otherwise.
 *
 * @params
 *   - uri: String - the url receiving the HTTP request
 *
 * @return: String
 *   a string representation of the HTTP get request
 *     - the string representation of the results if successful, error message otherwise
 */
suspend fun hitAPI(uri: String): String {
    val client = HttpClient()
    return try {
        val response: HttpResponse = client.get(uri)
        if (response.status.value == 200) {
            "${response.content}"
        } else { "There was error attempting to reach $uri"}
    } catch (e: Exception) {
        "Error fetching $uri."
    } finally {
        client.close()
    }
}
s

Sean Keane

03/16/2020, 7:13 PM
I think this is your issue.
Copy code
private fun fetcher(uri: String) = runBlocking<String> {
    val result = async {
        hitAPI(uri)
    }
    result.await()
}
If you change the
runBlocking
to be inside a
GlobalScope.launch { }
it should work. But, you should definitely use a different scope other than global. But this will test if thats the issue or not.
s

Shawn Karber_

03/16/2020, 7:16 PM
i was under the impression launch cannot return a value, is that incorrect?
figured it all out, thanks for the help
s

Sean Keane

03/16/2020, 7:51 PM
What was the issue