15

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

8 回答 8

11

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

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

于 2010-08-29T20:09:37.480 回答
5

这个错误似乎与非常有名的月球着陆器错误有关(在其上进行谷歌搜索)。这么久了,又发布了几个安卓版本,bug依旧存在,没有人去更新。我发现这可以使用最少的代码混乱:

  public void surfaceCreated(SurfaceHolder holder) {     
          if (thread.getState==Thread.State.TERMINATED) { 
               thread = new MainThread(getHolder(),this);
          }
          thread.setRunning(true);
          thread.start();
  }
于 2012-05-04T17:04:15.800 回答
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...
            }
        }
    }
于 2011-05-25T09:30:48.007 回答
2

我发现的最好方法是覆盖控制表面视图的活动的 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));
    }
于 2011-10-17T08:54:10.907 回答
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){}
}

}

我希望它会有所帮助。

于 2012-02-08T04:01:51.813 回答
1

试图评论上面接受的答案,但不能,这是新的。我认为您不应该同时从 SurfaceView 和 Activity 调用启动/停止线程方法。这将导致双重启动/停止线程,并且您不能多次启动线程。只需从 Activity 的 onPause 和 onResume 调用您的方法。它们在退出和重新进入应用程序时被调用,因此这将确保您的状态得到正确处理。surfaceDestroyed 并不总是被调用,这让我很困惑。

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

        while (_run) {
            if (_surfaceHolder.getSurface().isValid()) {
                ...
            }
        } //end _run
于 2011-08-03T14:30:21.420 回答
0

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

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

于 2010-08-20T03:19:40.380 回答
0

这个众所周知的问题的另一种解决方案。可悲的是,我不明白它为什么会起作用——它是意外出现的。但它对我来说很好,而且很容易实现:不需要重写Activity's onPause(), 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().

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

于 2011-06-04T17:13:31.257 回答