【问题标题】:how to pause and resume a surfaceView thread如何暂停和恢复surfaceView线程
【发布时间】:2011-04-01 10:54:35
【问题描述】:

我有一个 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;
    }

【问题讨论】:

    标签: android multithreading surfaceview


    【解决方案1】:

    简单的解决方案是简单地杀死并重新启动线程。创建方法 resume() - 创建线程对象并启动它 - 和 pause() - 杀死线程(参见 Lunarlander 示例) - 在 SurfaceView 类中并从 surfaceCreated 和 surfaceDestroyed 调用这些以启动和停止线程。

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

    【讨论】:

    • 我喜欢你的想法,我一直在努力寻找一些简单的东西。因为不是每次都调用“surfaceDestroyed”,而是“onPause”。就像按下“电源”按钮然后返回。所以我认为你的选择是一个非常好的选择。
    【解决方案2】:

    这个错误似乎与非常有名的月球着陆器错误有关(谷歌搜索)。经过这么长时间,经过几个android版本的发布,bug仍然存在并且 没有人费心去更新它。我发现这可以使用最少的代码混乱:

      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...
                }
            }
        }
    

    【讨论】:

    • 我正在学习surfaceview,但无法理解您为什么不在while 循环中使用break stamenet。我的程序在 onResume() 期间冻结,但在我将 break 重新启动后,显然创建了一个新线程。这是我的代码和问题:stackoverflow.com/questions/19200972/…
    • Activity onPause 不会总是自行调用 surfaceDestroyed。因此,仅此一项并不能解决问题。看到这个问题stackoverflow.com/q/11495842/1180117
    【解决方案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));
        }
    

    【讨论】:

    • 这对我有用。当我之前仅在 onResume 中尝试过它时,它不起作用。
    【解决方案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】:

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

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

      【讨论】:

      • 我已将其正确设置为在适当的地方使用 setRunning,但尽管我的 onResume 中有 thread.setRunning(true),但我的 surfaceView 在它回到最前沿时是空白的。该线程仍然存在,当我转到主屏幕时它不会使应用程序崩溃,如果我尝试另一个 thread.start() 我会收到错误消息,指出线程已启动。有什么想法吗?
      • 您只能启动一次线程。我不知道你的 setRunning 方法在内部做了什么,但在 Android 上正确停止线程的唯一方法是让它从它的 run() 方法返回。从那时起,您“必须”创建一个新的线程对象。你不能再使用那个线程了。如果要“暂停”线程,则必须使用 wait() 和 notifyAll()。以 Google 为例,并对此有适当的理解。
      【解决方案8】:

      这个众所周知的问题的另一种解决方案。可悲的是,我不明白它为什么会起作用——它是意外出现的。但它对我很有用,而且很容易实现:没有覆盖ActivityonPause()onResume()onStart()onStop(),也没有编写特殊的线程方法(如resume()、@987654328 @) 是必要的。

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

      要添加到渲染线程类的要点:

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

      很抱歉这么费空间的回答。我希望它能正常工作并有所帮助。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-12
      • 1970-01-01
      • 1970-01-01
      • 2016-03-24
      相关资源
      最近更新 更多