0

I am getting the following error:

suspend function 'getInfo' should be called only from a coroutine or another suspend function

Can you tell me what I'm doing wrong?

enter image description here

import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.util.Log
import com.myapp.test.WakeLocker.acquire
import com.myapp.test.WakeLocker.release
import org.json.JSONObject
import java.util.concurrent.ExecutionException

class WidgetService : BroadcastReceiver() {
    var json = JSONObject()
    override fun onReceive(context: Context, intent: Intent) {
        //wake the device
        acquire(context)

        //increase the number in the widget
        val preferences = context.getSharedPreferences("PREFS", 0)
        val urlCode = preferences.getString("urlCode", "")
        Log.d("WidgetService1", urlCode!!)
        if (urlCode != "") {
            val getPack = GetPack(urlCode)
            json = try {
                getPack.getInfo()
            } catch (e: ExecutionException) {
                throw RuntimeException(e)
            } catch (e: InterruptedException) {
                throw RuntimeException(e)
            }
            val editor = preferences.edit()
            editor.putString("json", json.toString())
            editor.apply()
        }

        //force widget update
        val widgetIntent = Intent(context, MyWidget::class.java)
        widgetIntent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
        val ids = AppWidgetManager.getInstance(context)
            .getAppWidgetIds(ComponentName(context, MyWidget::class.java))
        widgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
        context.sendBroadcast(widgetIntent)
        Log.d("WidgetService", "Widget set to update!")

        //go back to sleep
        release()
    }
}

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import org.jsoup.Jsoup
import java.io.IOException

class GetPack(id: String) {
    private val baseURL = "http://192.xxx.xxx.xxx"
    private val finalURL = "$baseURL:8080"

    suspend fun getInfo(): JSONObject = withContext(Dispatchers.IO) {
        val json = JSONObject()
        try {
            val document = Jsoup.connect(finalURL).get()
            val steps = JSONArray()
            val elements = document.select("#progressTracker-container > div")
            for (ele in elements) {
                val item = JSONObject()
                val reached = ele.attr("data-reached") == "reached"
                val message = ele.select(".milestone-primaryMessage").first().ownText()
                val data = ele.select(".milestone-primaryMessage > .nowrap").text()
                item.put("reached", reached)
                item.put("message", message)
                item.put("data", data)
                steps.put(item)
            }
            val primaryStatus = document.getElementById("primaryStatus")
            json.put("status", primaryStatus.text())
            json.put("steps", steps)
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
        json
    }
}
dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

    implementation 'org.jsoup:jsoup:1.11.2'
}
Paul
  • 3,644
  • 9
  • 47
  • 113
  • Does this answer your question? [Suspend function 'callGetApi' should be called only from a coroutine or another suspend function](https://stackoverflow.com/questions/53928668/suspend-function-callgetapi-should-be-called-only-from-a-coroutine-or-another) – dominicoder May 16 '23 at 17:17

1 Answers1

2

Your suspend fun getInfo() from GetPack class is mentioned with suspend. In Kotlin, suspend functions should be either called from suspend function or from Coroutines.

More regarding Coroutines: https://developer.android.com/kotlin/coroutines/coroutines-adv

Answer:

You can either do one of the following to resolve this error:

  1. Remove suspend from getInfo() and make it simple fun getInfo(). But my suggestion is don't do this as we are doing Json parsing here which might take time.
  2. Instead, in the WidgetService class you can start Coroutine with IO dispatcher as follow:
1. Add implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' in app build gradle. 
2. Use lifecycleScope.launch(Dispatchers.IO) {} as follow

        lifecycleScope.launch(Dispatchers.IO) {
            val getPack = GetPack(urlCode)
            json = try {
                getPack.getInfo()
            } catch (e: ExecutionException) {
                throw RuntimeException(e)
            } catch (e: InterruptedException) {
                throw RuntimeException(e)
            }
            val editor = preferences.edit()
            editor.putString("json", json.toString())
            editor.apply()
        }

Shubham Hupare
  • 321
  • 1
  • 3
  • 11
  • Ok, but: https://i.stack.imgur.com/dhsHF.png – Paul May 16 '23 at 16:15
  • Ohh. You can then use `GlobalScope.launch(Dispatchers.IO) { //remaining code as it is}`. But the best practice is use a scope which is lifecycle aware hence here you can do as follow: 1. Add `implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'` in app build gradle. 2. Use lifecycleScope.launch(Dispatchers.IO) {//remaining code as it is } – Shubham Hupare May 16 '23 at 16:42
  • Can you edit the answer so that it doesn't bother me. – Paul May 16 '23 at 16:47
  • Edited in above answer for fullfillness. – Shubham Hupare May 16 '23 at 16:51
  • How should I declare the variable, it doesn't let me import any library. https://i.stack.imgur.com/uKjuU.png – Paul May 16 '23 at 19:19
  • Does not need to create a variable you have to just add `implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'` in `app build gradle` under `dependencies {}` section. Once you do this, you can directly use `lifecycleScope` in any Activity or Fragment. ` – Shubham Hupare May 17 '23 at 03:40
  • Here: https://i.stack.imgur.com/McUb7.png – Paul May 17 '23 at 07:14
  • But it gives me error when I put it in the code, it tells me to declare the variable. – Paul May 17 '23 at 07:17