Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kotlin coroutine throws java.lang.IllegalStateException: Already resumed, but got value Location

I'm quite new to Kotlin coroutine and Android development in general. While playing around to understand how it worked, I faced an error I can't seem to solve.

From a basic activity i try to connect to the googleApiClient. The permissions are ok. I wish to use kotlin coroutines to get location updates from LocationManager in a direct style to use this Location object later on. The first time I changed my position in the emulator it works fine, the second time I change my position, It crashes with an exception like this:

FATAL EXCEPTION: main
    Process: com.link_value.eventlv, PID: 32404
    java.lang.IllegalStateException: Already resumed, but got value Location[gps 48.783000,2.516180 acc=20 et=+59m16s372ms alt=0.0 {Bundle[mParcelledData.dataSize=40]}]
    at kotlinx.coroutines.experimental.AbstractContinuation.resumeImpl(AbstractContinuation.kt:79)
    at kotlinx.coroutines.experimental.AbstractContinuation.resume(AbstractContinuation.kt:72)
    at com.link_value.eventlv.View.Create.NewEventLvActivity$await$2$1.onLocationChanged(NewEventLvActivity.kt:100)
    at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:297)
    at android.location.LocationManager$ListenerTransport.-wrap0(LocationManager.java)
    at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:242)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:154)
     at android.app.ActivityThread.main(ActivityThread.java:6077)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)



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

    askForUserLocation()
    val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    val presenter = CreateEventPresenterImpl(this@NewEventLvActivity)

    googleApiClient = GoogleApiClient.Builder(this@NewEventLvActivity)
            .enableAutoManage(this /* FragmentActivity */,
                    this /* OnConnectionFailedListener */)
            .addApi(Places.GEO_DATA_API)
            .addConnectionCallbacks(this)
            .build()
}
override fun onConnected(p0: Bundle?) {
    val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    input_address.addTextChangedListener(object: TextWatcher{
        override fun afterTextChanged(p0: Editable?) {
        }

        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }

        override fun onTextChanged(query: CharSequence?, p1: Int, p2: Int, p3: Int) {
            if (query.toString().length >= 4) {
                launch(UI) {
                    val locationUpdated = locationManager.await(LocationManager.GPS_PROVIDER)
                    input_name.text = Editable.Factory.getInstance().newEditable(locationUpdated.toString())
                }
            }
        }
    })
}

private suspend fun LocationManager.await(locationProvider: String): Location? = suspendCoroutine { cont ->
    try {
        requestLocationUpdates(locationProvider, 0, 0.toFloat(), object : LocationListener {
            override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
            }

            override fun onProviderEnabled(p0: String?) {
            }

            override fun onProviderDisabled(p0: String?) {
                cont.resumeWithException(UnknownLocationException())
            }

            override fun onLocationChanged(location: Location?) {
                cont.resume(location)
            }
        })
    } catch (ex: SecurityException) {
        cont.resumeWithException(ex)
    }
}

It's as if Kotlin use the same Continuation. I don't know what I'm doing wrong and why it crashes the second time. Can someone enlighten me. Thks in advance.

like image 668
Shinmen Avatar asked Jan 12 '18 13:01

Shinmen


People also ask

How to handle exceptions in coroutines?

To ignore any exceptions, launch the parent coroutine with the async function; however, if required to handle, the exception uses a try-catch block on the await() call on the Deferred object returned from async coroutine builder. When using launch builder the exception will be stored in a Job object.

What is suspendCancellableCoroutine?

This function throws a CancellationException if the Job of the coroutine is cancelled or completed while it is suspended. A typical use of this function is to suspend a coroutine while waiting for a result from a single-shot callback API and to return the result to the caller.

What is supervisor scope Kotlin?

supervisorScopeThis function returns as soon as the given block and all its child coroutines are completed. Unlike coroutineScope, a failure of a child does not cause this scope to fail and does not affect its other children, so a custom policy for handling failures of its children can be implemented.


1 Answers

According to documentation, it's possible to resume Continuation only single time. Second resume will throw IllegalStateException with "Already resumed, but got $proposedUpdate" message. You can add additional checking continuation.isActive to prevent this exception. It's better to use Channels for multiple callbacks like location updates.

like image 143
Mikhail Sharin Avatar answered Oct 04 '22 11:10

Mikhail Sharin