Search code examples
kotlinandroid-activitysurfaceviewkotlin-coroutinesandroid-toast

How to use Toast message from inside SurfaceView inside GameActivity?


Following are snippets of the GameActivity and GameView in my project: GameActivity.kt

class GameActivity : Activity() {
    private var gameView: GameView? = null

    // We override the oncreate to add the gameview which will be showing the game
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var display = windowManager.defaultDisplay

        val size = Point()
        display.getSize(size)

        gameView = GameView(this, size)
        setContentView(gameView)
    }
}

GameView.kt

class GameView(context: Context, private val size: Point) : SurfaceView(context), Runnable {

    private val gameThread = Thread(this)

    ...

    private fun prepareLevel() {}

    ...
    private fun update (fps: Long) {
    if (RectF.intersects(powerup.position, player.position)) {
        Toast.makeText(context, "Powerup intersects", Toast.LENGTH_SHORT).show()
    }
    }
    ...
}

The problem is when the intersection happens the code breaks with the following error:

E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.example.sacredanimals, PID: 3367
    java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
        at android.widget.Toast$TN.<init>(Toast.java:390)
        at android.widget.Toast.<init>(Toast.java:114)
        at android.widget.Toast.makeText(Toast.java:277)
        at android.widget.Toast.makeText(Toast.java:267)
        at com.example.sacredanimals.GameView.update(GameView.kt:96)
        at com.example.sacredanimals.GameView.run(GameView.kt:78)
        at java.lang.Thread.run(Thread.java:764)

I did some debugging, and probably the issue is happening because the Toast message should be called on the main thread rather on one of the child threads, as suggested in the link: Can't toast on a thread that has not called Looper.prepare() But, I am unable to resolve this for the case where I want to call the Toast.message from inside SurfaceView


Solution

  • Toast.makeText() should only be called from Main/UI thread. Looper.getMainLooper() helps you to achieve it:

    Handler(Looper.getMainLooper()).post { 
         Toast.makeText(context, "Powerup intersects", Toast.LENGTH_SHORT).show()
    }