I'm attempting to write data from a list to a specific Google Sheet. I've set up the configuration, but I'm encountering an error that seems related to not using a coroutine.
Is my approach correct? Is there a way to achieve this without coroutines, as I'm unsure how to implement them? If coroutines are necessary, how can I integrate them properly?
The error occurs when triggering a button on the device, leading to an app shutdown:
FATAL EXCEPTION: main
Process: com.nase.naseapp, PID: 26027
android.os.NetworkOnMainThreadException
...
...
...
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@13df640, androidx.compose.runtime.BroadcastFrameClock@de98979, StandaloneCoroutine{Cancelling}@e896be, AndroidUiDispatcher@5ea431f]
The problematic class contains the method called by a button's onClick
function:
class SheetsAPI(private val application: Application, credential: GoogleCredential) {
private val jsonFactory = JacksonFactory.getDefaultInstance()
private val httpTransport = NetHttpTransport()
private val service: Sheets = Sheets.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(application.getString(R.string.app_name))
.build()
fun submitExpensesToGoogleSheet(expenses: List<ExpenseData>){
val spreadsheetId = "_____spreadsheetid____"
val range = "Sheet1!A1:B2"
val valueRange = ValueRange().setValues(
listOf(
listOf("Expense", "Amount"),
*expenses.map { listOf(it.title, it.amount) }.toTypedArray()
)
)
service.spreadsheets().values().update(spreadsheetId, range, valueRange)
.setValueInputOption("RAW")
.execute()
println("Sending data to Sheet")
}
}
Any insights on why the NetworkOnMainThreadException
occurs and guidance on implementing coroutines or alternatives would be highly appreciated. Thank you!
As you have stated and have shared, you are receiving a NetworkOnMainThreadException. This exception is thrown when:
"...an application attempts to perform a networking operation on its main thread." Reference
You are getting this exception specifically due to your logic in the following lines:
service.spreadsheets().values().update(spreadsheetId, range, valueRange)
.setValueInputOption("RAW")
.execute()
The call above is one that performs a network request and you cannot do those on the main thread as they:
To overcome this, you can use Coroutines to handle the calling and getting of the result of the network request. There are other components you can use as well.
An example of how you would wrap your logic in a Coroutine would be as follows:
suspend fun submitExpensesToGoogleSheet(expenses: List<ExpenseData>) {
val spreadsheetId = "_____spreadsheetid____"
val range = "Sheet1!A1:B2"
val valueRange = ValueRange().setValues(
listOf(
listOf("Expense", "Amount"),
*expenses.map { listOf(it.title, it.amount) }.toTypedArray()
)
)
withContext(Dispatchers.IO) {
service.spreadsheets().values().update(spreadsheetId, range, valueRange)
.setValueInputOption("RAW")
.execute()
println("Sending data to Sheet")
}
}
}
Notice that you will need to make your method a suspend method and call that from either another suspend method or inside of a Coroutine. You can read more about Coroutines here.