2010-08-20 645 views
14

我有一个surfaceView设置和运行,但是当我恢复它时,我得到一个线程已经启动的错误。什么是应用程序进入后台然后回到前台的正确方法?我修了一下,并设法让应用程序回来而不会崩溃...但SurfaceView不再绘制对象。我的代码:如何暂停和恢复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; 
    } 

回答

11

简单的解决方案是简单地杀死并重启线程。在SurfaceView类中创建方法resume() - 创建线程对象并启动线程对象并暂停() - 杀死线程(请参阅Lunarlander示例)并从su​​rfaceCreated和surfaceDestroyed调用它们以启动和停止线程。

现在在运行SurfaceView的Activity中,还需要从Activity(或片段)onResume()和onPause()调用SurfaceView中的resume()和pause()方法。这不是一个优雅的解决方案,但它会起作用。

+0

我爱Ur的想法,我一直在努力寻找一些容易。因为“surfaceDestroyed”不是每次都调用,而是“onPause”。就像按下“电源”按钮然后返回。所以我认为你的选择是一个非常好的选择。 – 2010-10-30 02:34:49

0

您应使用活动的onPause()和的onResume()方法。

首先,在surfaceCreated()中启动线程。此外,在onResume()中,确保线程尚未启动(在线程内部保留一个变量)。如果它没有运行,请将其设置为再次运行。在onPause()中,暂停该线程。在surfaceDestroyed中,再次暂停该线程。

+0

我有它正确设置在适当的地方使用setRunning,但尽管有thread.setRunning(真)在我的onResume我surfaceView为空时,其背在最前沿。线程仍然存在,当我进入主屏幕时它不会崩溃应用程序,并且如果我确实尝试了另一个线程。start(),我会看到线程已启动的错误。有任何想法吗? – jfisk 2010-08-20 18:35:24

+0

您只能启动一次线程。我不知道你的setRunning方法在内部做了什么,但是在Android上正确停止线程的唯一方法是让它从run()方法返回。从那时起,你必须创建一个新的线程对象。你不能再次使用该线程。如果你想'暂停'线程,你必须使用wait()和notifyAll()。谷歌周围例如和正确的理解。 – Moncader 2010-08-21 01:15:07

2
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... 
      } 
     } 
    } 
+0

我正在学习surfaceview,但不能理解你为什么不在while循环中使用break stamenet。我的程序在onResume()期间冻结了,但是在我放置了break之后,它开始了agin,显然创建了一个新的线程。这是我的代码和问题:http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther 2013-10-05 21:17:39

+0

阅读Thread.join(),那么你会明白。 – 2013-10-08 02:52:21

+0

活动'onPause'不会总是**自己调用'surfaceDestroyed'。所以这一点就不能解决问题。看到这个问题http://stackoverflow.com/q/11495842/1180117 – Kiran 2015-03-10 09:42:09

0

这个好已知的问题的另一个解决方案。可悲的是,我不明白它为什么有效 - 它意外地出现了。但它适用于我,并且很容易实现:不需要重写ActivityonPause()onResume()onStart()onStop(),也不需要编写特殊线程方法(如resume(),pause())。

特殊要求是将所有变化的变量放在渲染线程类以外的东西上。

要点添加到渲染线程类:

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()

对不起,这种空间消耗的答案。我希望它能正常工作,并会有所帮助。

+0

活动'onPause'不会总是**自己调用'surfaceDestroyed'。所以这不会解决问题。看到这个问题http://stackoverflow.com/q/11495842/1180117 – Kiran 2015-03-10 09:37:30

1

试图评论上面接受的答案,但不能,新的这一点。我不认为你应该从SurfaceView和Activity中调用你的开始/停止线程方法。这将导致双重启动/停止线程,并且不能多次启动线程。只需从Activity的onPause和onResume中调用您的方法即可。他们在退出并重新进入应用程序时被调用,以确保您的状态得到正确处理。 SurfaceDestroyed并不总是被调用,这让我困惑了一会儿。

如果使用此方法,请确保在处理画布之前检查运行代码中的有效表面,因为活动将在表面可用之前启动onResume中的线程。

 while (_run) { 
      if (_surfaceHolder.getSurface().isValid()) { 
       ... 
      } 
     } //end _run 
+0

收回的评论 – tjb 2011-10-17 08:51:30

2

我已经发现,最好的办法是重写活动控制表面视图,以便用它的方法重新实例化SurfaceView的方法的onResume,然后用的setContentView设置它。这种方法的问题是您需要重新加载SurfaceView正在处理的任何状态。

public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(new MyCustomSurfaceView(this)); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     setContentView(new MyCustomSurfaceView(this)); 
    } 
+0

这对我来说伎俩。当我在onResume之前尝试它时,它不起作用。 – sivi 2014-03-13 19:27:28

1

这是我用过的。该应用程序现在不崩溃。

视图类:

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){} 
} 

} 

我希望这将有助于。

+0

活动'onPause'不会总是**自己调用'surfaceDestroyed'。所以这不会解决问题。看到这个问题http://stackoverflow.com/q/11495842/1180117 – Kiran 2015-03-10 09:38:58

4

此错误似乎与月球着陆器错误有关,这个错误非常有名(对Google进行搜索)。毕竟这一次,以及几个Android版本发布后,该错误依然存在,并且没有人打扰更新它。我发现这个用最少的代码杂乱的工作:

public void surfaceCreated(SurfaceHolder holder) {  
      if (thread.getState==Thread.State.TERMINATED) { 
       thread = new MainThread(getHolder(),this); 
      } 
      thread.setRunning(true); 
      thread.start(); 
    } 
+0

活动'onPause'不会总是**自己调用'surfaceDestroyed'。所以这只会解决问题。看到这个问题http://stackoverflow.com/q/11495842/1180117 – Kiran 2015-03-10 09:43:19