К сожалению, MyAppName остановился. Поток уже запущен — Kotlin

#android #multithreading #kotlin

#Android #многопоточность #котлин

Вопрос:

Мне действительно нужна помощь здесь. Извините, что спрашиваю об этом, что может показаться простым. Речь идет о потоках. Я только начинаю узнавать об этом. Я просмотрел весь Интернет, но ничего не решило мою проблему.

Мое приложение работает, когда я начинаю его запускать. Но, если он потеряет фокус и возобновится, произойдет сбой. Он выдает «К сожалению, MyAppName остановлено». и я получил «java.lang.Исключение IllegalThreadStateException: поток уже запущен «.

Это моя основная активность:

     class MainActivity : AppCompatActivity() {
    private var snakeGame: SnakeGame? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val size = Point()
        supportActionBar?.hide()
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        windowManager.defaultDisplay.getSize(size)
        snakeGame = SnakeGame(this, size)
        snakeGame?.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
        setContentView(snakeGame)
    }


    override fun onResume() {
        super.onResume()
        snakeGame?.resume()
    }

    override fun onPause() {
        super.onPause()
        snakeGame?.pause()
    }
}

  

Это мой просмотр игры:

 
@SuppressLint("ViewConstructor")
class SnakeGame(context: Context, size: Point)
    : SurfaceView(context), Runnable {
    private lateinit var touchDown: Point
    private lateinit var arena: Arena
    private lateinit var snake: Snake
    private lateinit var food: Food

    private val bgColor = Color.argb(255, 0, 0, 0)
    private val buttons = ArrayList<ControlButton>()
    private var gameThread = Thread(this)
    private val w = size.x
    private val h = size.y

    private var playing = false
    private var paused = true
    private var border = Rect()
    private var score = 0
    private var gameSpeed = 150


    override fun run() {
        var sameFrame = false
        var sfTime = 0L
        var acmTime: Long
        while (playing) {
            if (!paused) {
                if (sameFrame) {
                    acmTime = System.currentTimeMillis() - sfTime
                    if (acmTime > gameSpeed) {
                        update()
                        sameFrame = false
                    }
                } else {
                    sfTime = System.currentTimeMillis()
                    sameFrame = true
                }
            }
            draw()
        }
    }

    private fun setup() {
        arena = Arena(w - arenaCenter, h / 2, arenaW, arenaH)
        border = arena.sq
        snake = Snake(border.left, border.top)
        food = Food(border.left, border.top)
        if (buttons.size < 1) {
            createButtons(h)
        }
    }

    private fun update() {
        snake.update(border.left, border.top, border.right, border.bottom)
        if (snake.die()) {
            paused = true
        }
        if (snake.eat(food.fPos)) {
            food = Food(border.left, border.top)
            score  
        }
    }

    private fun draw() {
        if (holder.surface.isValid) {
            c = holder.lockCanvas()
            c.drawColor(bgColor)
            val paint = Paint().apply {
                color = Color.WHITE
                textSize = 70f
            }
            c.drawText("Score: $score", 20f, 50f, paint)

            arena.show()
            food.show()
            snake.show()
            for (cb in buttons) {
                cb.show()
            }
            holder.unlockCanvasAndPost(c)
        }
    }

    fun pause() {
        playing = false
        try {
            gameThread.join()
        } catch (e: InterruptedException) {
            Log.e("Error", "joining thread")
        }
    }

    fun resume() {
        playing = true
        setup()
        gameThread.start()
    }
}

  

Комментарии:

1. Как вы справляетесь с этим в методах onStop / onDestroy? из журналов сбоев я могу сказать, что вы пытаетесь снова запустить поток, который уже запущен.

2. Поток может быть запущен только один раз за все время существования потока — если вы намерены позволить игровому потоку завершиться на паузе (воспроизведение = false с последующим присоединением), тогда поток необходимо воссоздать в resume .

3. Как мне это сделать? Я действительно растерялся по этому поводу. Когда я покидаю приложение, я не могу вернуться к нему.

4. Он resume завершается сбоем в строке gameThread.start() — поэтому непосредственно перед этим воссоздайте поток — как в gameThread = Thread(this) .

5. @Andy, это было оно. Теперь я могу лучше понимать и практиковать. Мне все еще нужно больше узнать о потоках, но это мне очень поможет. Спасибо!

Ответ №1:

Потому что один поток может быть запущен один раз, но вы вызываете .join() метод, который останавливает поток, а затем пытается запустить его во второй раз.

Если вы хотите приостановить поток, вам следует изменить свои .pause() .resume() методы и:

     fun pause() {
        paused = true
        //remove this block
        /*playing = false
        try {
            gameThread.join()
        } catch (e: InterruptedException) {
            Log.e("Error", "joining thread")
        }*/
    }

    fun resume() {
        paused = false
        //setup() removing this too
        //gameThread.start()
    }
  

Также добавьте методы .startGame() и .stopGame() :

    fun startGame() {
      setup()
      gameThread.start()  
   } 
   
   fun stopGame() {
      try {
        gameThread.join()
      } catch (e: InterruptedException) {
        Log.e("Error", "joining thread")
      }    
   }
  

И вызовите эти методы в activity onCreate() и onDestroy() :

 class MainActivity : AppCompatActivity() {
    private var snakeGame: SnakeGame? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val size = Point()
        supportActionBar?.hide()
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        windowManager.defaultDisplay.getSize(size)
        snakeGame = SnakeGame(this, size)
        snakeGame?.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
        setContentView(snakeGame)
        snakeGame?.startGame()
    }


    override fun onResume() {
        super.onResume()
        snakeGame?.resume()
    }

    override fun onPause() {
        super.onPause()
        snakeGame?.pause()
    }

    override fun onDestroy() {
        super.onDestroy()
        snakeGame?.stopGame()
    }
}
  

.stopGame() метод закроет поток и освободит ресурсы, когда ваша активность вот-вот умрет

Комментарии:

1. Это не сработало. С этим исправлением приложение вылетает, как только я его покидаю.

2. Я буду использовать это, чтобы улучшить свой код, но мне нужно изменить пару вещей, чтобы заставить его работать. Спасибо за быстрый ответ! Я опубликую окончательную версию.