如何暂停和恢复surfaceView线程

问题描述 投票:0回答:8

我有一个 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
8个回答
12
投票

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

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


5
投票

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

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

2
投票

我发现的最好方法是重写控制 Surface View 的 Activity 的 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));
    }

1
投票

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

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

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

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
投票

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

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


0
投票

这个众所周知的问题的另一个解决方案。可悲的是,我不明白它为什么起作用——它是意外出现的。但它对我来说效果很好,而且很容易实现:不需要重写

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

很抱歉回答这么耗时。我希望它能正常工作并有所帮助。


0
投票

你真的不需要做任何事情,真的。 Surface 只是想抱怨它已经在运行,并且它通过抛出错误来做到这一点。将其包装在 try catch 中并忽略错误。

private suspend fun SurfaceView.prepareSurface(): Surface = suspendCoroutine { cont ->
        val callback = object : SurfaceHolder.Callback {
            override fun surfaceDestroyed(holder: SurfaceHolder) {
            }

            override fun surfaceChanged(
                holder: SurfaceHolder,
                format: Int,
                width: Int,
                height: Int
            ) = onSurface(holder)

            override fun surfaceCreated(holder: SurfaceHolder) = onSurface(holder)

            private fun onSurface(holder: SurfaceHolder) {
                post {
                    try {
                        if (holder.surface.isValid) cont.resume(holder.surface)
                    } catch (e: IllegalStateException) {
                        // Ignore error to persist UI config changes
                    }
                }
            }
        }
        holder.addCallback(callback)
    }
© www.soinside.com 2019 - 2024. All rights reserved.