2

I'm developing my first app and learning as I go. The app communicates with a URL to fetch data, and sometimes to send data in a POST. Since my last gradle update to 7.1.1, my app is crashing when I open 2 different activities where such a communication is happening.

Here's the error I'm getting:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.creoconcept.sifoa, PID: 7255
java.lang.IllegalStateException: No activity
    at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1366)
    at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2841)
    at androidx.fragment.app.FragmentManager.dispatchPause(FragmentManager.java:2802)
    at androidx.fragment.app.FragmentController.dispatchPause(FragmentController.java:295)
    at androidx.fragment.app.FragmentActivity.onPause(FragmentActivity.java:366)
    at com.creoconcept.sifoa.DisplayMyQrCode.onPause(DisplayMyQrCode.kt:63)
    at com.creoconcept.sifoa.DisplayMyQrCode$checkCodeScanned$1$run$1.invoke(DisplayMyQrCode.kt:37)
    at com.creoconcept.sifoa.DisplayMyQrCode$checkCodeScanned$1$run$1.invoke(DisplayMyQrCode.kt:26)
    at com.creoconcept.sifoa.Confs$communicateWithSifoaApi$1$1.invokeSuspend(Confs.kt:64)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

I/Process: Sending signal. PID: 7255 SIG: 9

It looks like it has something to do with coroutines. I'm used to doing the same type of thing in jQuery, using ajax or $.get(), and don't quite understand this coroutines thing yet. I'm just trying to fetch some JSON from a URL, and it seems to be a much more complicated thing in Kotlin/Android than it is in jQuery.

Here's some of my code:

fun communicateWithSifoaApi(url:String, callback: (newRef: String) -> Unit){
    CoroutineScope(Dispatchers.Default).launch {
        withContext(Dispatchers.IO) {
            callback(URL(api_url + url).readText())
        }
    }
}

Here's how I'm doing POSTs:

fun sendPostToApi(url:String, dataString:String, callback: (response: String) -> Unit){
    val mURL = URL(api_url + url)
    CoroutineScope(Dispatchers.Default).launch {
        withContext(Dispatchers.IO) {
            with(mURL.openConnection() as HttpURLConnection) {
                requestMethod = "POST"
                val wr = OutputStreamWriter(outputStream)
                wr.write(base64encoding.base64EncodeString(dataString))
                wr.flush()
                BufferedReader(InputStreamReader(inputStream)).use {
                    val response = StringBuffer()
                    var inputLine = it.readLine()
                    while (inputLine != null) {
                        response.append(inputLine)
                        inputLine = it.readLine()
                    }
                    callback(response.toString())
                }
            }
        }
    }
}

I used to have GlobalScope.launch here, but after the update I started getting the "delicate API" message so I changed it to CoroutineScope(Dispatchers.Default).launch after some Googleing. Before that I was using doAsync which seemed the most similar to jQuery's Ajax, but that was deprecated.

There's also an error now in FragmentManager.java, it now since the update says Cannot resolve symbol 'R' for androidx.fragment.R. I don't know if this has something to do with the problem.

I'd appreciate any help. Thanks.

fragment xml:

    <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".GoToShoppingCartButton">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/goToShoppingCartButton"
        android:layout_width="70sp"
        android:layout_height="70sp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:backgroundTint="@color/buyButtonColor"
        android:clickable="true"
        android:contentDescription="@string/checkout"
        android:src="@drawable/cart"
        android:tint="@color/buyButtonTextColor"
        app:backgroundTint="@color/buyButtonColor"
        app:fabCustomSize="70sp"
        app:fabSize="auto"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</FrameLayout>

Fragment .kt file:

package com.creoconcept.sifoa

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.material.floatingactionbutton.FloatingActionButton

class GoToShoppingCartButton : Fragment(R.layout.fragment_go_to_shopping_cart_button) {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_go_to_shopping_cart_button, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<FloatingActionButton>(R.id.goToShoppingCartButton).setOnClickListener {
            activity?.let {
                val intent = Intent(it, ShoppingCart::class.java).apply {}
                it.startActivity(intent)
            }
        }
    }

}

code in a different activity, where the fragment is called:

private fun refreshGoToCartButton(){
    val buttonFragment =supportFragmentManager.findFragmentByTag("goToCartButton")
    if(buttonFragment != null) {
        val ft = supportFragmentManager.beginTransaction()
        if (shoppingCartArray.isNotEmpty()) {
            ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out).show(buttonFragment).commit()
        } else {
            ft.hide(buttonFragment).commit()
        }
    } else if (shoppingCartArray.isNotEmpty()) {
        supportFragmentManager.commit {
            setReorderingAllowed(true)
            add<GoToShoppingCartButton>(R.id.browseImgsWrapper,"goToCartButton")
        }
    }
}

Please note though that the crash happens even when i never even open the activity where this fragment is being used.

sazira
  • 31
  • 3
  • "There's also an error now in FragmentManager.java, it now since the update says Cannot resolve symbol 'R' for androidx.fragment.R" This is because your code was not build properly. Clean and then compile to resolve this issue. – Taranmeet Singh Feb 10 '22 at 12:10
  • The crash is related to illegal state of fragment can you add code where you have added fragment and done fragment transaction. – Taranmeet Singh Feb 10 '22 at 12:14
  • @TaranmeetSingh i've done that multiple times, also rebuilt it, and even "invalidate caches". nothing fixed that error. – sazira Feb 10 '22 at 12:43
  • Is your code compiling ? are you able to compile apk if so then it could be some issue with IDE – Taranmeet Singh Feb 10 '22 at 12:44
  • @TaranmeetSingh i've added the code related to the fragment – sazira Feb 10 '22 at 12:52
  • @TaranmeetSingh i am able to build the APK – sazira Feb 10 '22 at 12:53
  • Do you mind adding the activity code? – Evans Chepsiror Feb 10 '22 at 13:32
  • Anyway, replacing `GlobalScope` with `CoroutineScope().launch()` changes nothing. The main point of having scopes is to manage their lifecycle and failures, so we don't leak background operations and don't ignore errors silently. If you don't do this, you can as well use `GlobalScope` and suppress warnings - it's the same. Proper fix is to use one of existing scopes that have a properly managed lifecycle: `lifecycleScope`, `viewModelScope`, etc. – broot Feb 10 '22 at 13:55
  • @broot thanks, so how do I replace CoroutineScope().launch() with one of those, in kotlin, in a way that works properly? – sazira Feb 10 '22 at 15:05
  • I'm not an Android guy, but I believe Activity/Fragment have `lifecycleScope` property that is attached to their lifecycle and ViewModel has similar `viewModelScope` prop. You probably need to add some additional libs if you didn't yet. Other than that, I think you should find clear answers by checking Google for these prop names. – broot Feb 10 '22 at 15:09

1 Answers1

1

ok so it seems to be fixed now. It apparently had nothing to do with the FragmentManager (that Cannot resolve symbol 'R' for androidx.fragment.R error seems to be an IDE error and I've chosen to ignore it..)

one of the lines in the crash report is:

at com.creoconcept.sifoa.DisplayMyQrCode.onPause(DisplayMyQrCode.kt:63)

I was calling the onPause() function in a loop that was checking if the QR code had been scanned every 2 seconds. When it returns true the app is supposed to say "Code scanned" and then return to the main activity. I removed the onPause() call and replaced it with mainHandler.removeCallbacksAndMessages(null) and now it seems to not crash anymore.

sazira
  • 31
  • 3