Как приостановить и возобновить поток surfaceView
У меня есть настройка и запуск surfaceView, но когда я его возобновляю, я получаю сообщение об ошибке, когда поток уже запущен. Каким образом можно обращаться, когда приложение переходит на задний план, а затем обратно на передний план? Ive возился вокруг и сумел заставить приложение вернуться без сбоев... но surfaceView больше не рисует ничего. Мой код:
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("sys","surfaceCreated was called.");
if(systemState==BACKGROUND){
thread.setRunning(true);
}
else {
thread.setRunning(true);
thread.start();
Log.e("sys","started thread");
systemState=READY;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("sys","surfaceDestroyed was called.");
thread.setRunning(false);
systemState=BACKGROUND;
}
Ответы
Ответ 1
Простое решение - просто убить и перезапустить поток. Создание методов resume() - создает объект потока и запускает его - и pause() - убивает поток (см. Пример Lunarlander) - в вашем классе SurfaceView и вызывайте их из surfaceCreated и surfaceDestroyed, чтобы запустить и остановить поток.
Теперь в Activity, который запускает SurfaceView, вам также необходимо вызвать методы resume() и pause() в SurfaceView из Activity (или фрагмента) onResume() и onPause(). Это не элегантное решение, но оно будет работать.
Ответ 2
Эта ошибка, по-видимому, связана с ошибкой лунного помещика, которая довольно известна (выполните поиск Google). После всего этого времени, и после нескольких выпусков версии для Android, ошибка все еще существует и
никто не удосужился обновить его. Я нашел, что это работает с наименьшим беспорядком кода:
public void surfaceCreated(SurfaceHolder holder) {
if (thread.getState==Thread.State.TERMINATED) {
thread = new MainThread(getHolder(),this);
}
thread.setRunning(true);
thread.start();
}
Ответ 3
public void surfaceCreated(SurfaceHolder holder) {
if (!_thread.isAlive()) {
_thread = new MyThread(this, contxt);
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
Ответ 4
Лучший способ, который я нашел, - переопределить метод onResume активности, контролирующий поверхностное представление, так что с помощью метода он повторно создает SurfaceView, а затем устанавливает его с помощью setContentView. Проблема с этим подходом заключается в том, что вам нужно перезагрузить любое состояние, о котором заботился SurfaceView.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyCustomSurfaceView(this));
}
@Override
protected void onResume() {
super.onResume();
setContentView(new MyCustomSurfaceView(this));
}
Ответ 5
Пытался прокомментировать принятый ответ выше, но не смог, новичок в этом. Я не думаю, что вы должны называть свои методы запуска/остановки потоков как с SurfaceView, так и с Activity. Это приведет к запуску/остановке потока вдвойне, и вы не можете запускать поток более одного раза. Просто вызовите свои методы из Activity onPause и onResume. Они вызывается при выходе и повторном входе в приложение, чтобы это обеспечивало правильное управление вашими состояниями. surfaceDestroyed не всегда называется, что напутало меня на некоторое время.
Если вы используете этот метод, убедитесь, что перед тем, как работать с вашим холстом, проверьте действительную поверхность в своем коде запуска, потому что Activity запустит поток в onResume до того, как поверхность будет доступна.
while (_run) {
if (_surfaceHolder.getSurface().isValid()) {
...
}
} //end _run
Ответ 6
Это то, что я использовал. Приложение не вылетает сейчас.
Класс просмотра:
holder.addCallback(new Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.setRunning(false);
gameLoopThread.stop();
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
В GameLoopThread:
private boolean running = false;
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
long ticksPs=1000/FPS;
long startTime;
long sleepTime;
while(running){
Canvas c = null;
startTime=System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime=ticksPs-(System.currentTimeMillis()-startTime);
try{
if(sleepTime>0){
sleep(sleepTime);
}
else
sleep(10);
} catch(Exception e){}
}
}
Надеюсь, это поможет.
Ответ 7
Вы должны использовать методы Activities onPause() и onResume().
Сначала, в surfaceCreated(), запустите поток. Кроме того, в onResume() убедитесь, что поток еще не запущен (сохраняйте переменную внутри потока или что-то еще). Затем, если он не запущен, установите его как запущенный снова. в onPause() приостановите поток. В surfaceDestroyed снова приостановите поток.
Ответ 8
Другое решение этой хорошо известной проблемы. К сожалению, я не понимаю, почему это работает - это получилось случайно. Но он работает хорошо для меня, и его легко реализовать: без переопределения Activity
onPause()
, onResume()
, onStart()
, onStop()
и написания специальных методов потока (например, resume()
, pause()
)) необходимы.
Особое требование - поместить все изменяющиеся переменные в нечто иное, чем рендеринг класса потока.
Основные моменты для добавления в класс render-thread:
class RefresherThread extends Thread {
static SurfaceHolder threadSurfaceHolder;
static YourAppViewClass threadView;
static boolean running;
public void run (){
while(running){
//your amazing draw/logic cycle goes here
}
}
}
Теперь важные вещи о YourAppViewClass
:
class YourAppViewClass extends SurfaceView implements SurfaceHolder.Callback {
static RefresherThread surfaceThread;
public YourAppViewClass(Activity inpParentActivity) {
getHolder().addCallback(this);
RefresherThread.threadSurfaceHolder = getHolder();
RefresherThread.threadView = this;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceThread = new RefresherThread();
surfaceThread.running=true;
surfaceThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceThread.running=false;
try {
surfaceThread.join();
} catch (InterruptedException e) {
}
}
}
Два вышеперечисленных кодовых блока не являются полностью написанными классами, а простое представление о том, какие команды необходимы. Также обратите внимание, что каждый возврат в приложение вызывает surfaceChanged()
.
Извините за такой многопользовательский ответ. Я надеюсь, что он будет работать правильно и поможет.