#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. Я буду использовать это, чтобы улучшить свой код, но мне нужно изменить пару вещей, чтобы заставить его работать. Спасибо за быстрый ответ! Я опубликую окончательную версию.