Android 如何暂停和恢复surfaceView线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3527621/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to pause and resume a surfaceView thread
提问by jfisk
I have a surfaceView setup and running, but when I resume it I get an error that the thread has already been started. What's the proper way to handle when the app goes to the background and then back to the foreground? I've tinkered around and managed to get the app to come back without crashing... but the surfaceView doesn't draw anything anymore. My code:
我有一个 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;
}
采纳答案by Michael A.
The easy solution is to simply kill and restart the thread. Create methods resume() - creates thread object and starts it - and pause() - kills thread (see Lunarlander example) - in your SurfaceView class and call these from surfaceCreated and surfaceDestroyed to start and stop the thread.
简单的解决方案是简单地杀死并重新启动线程。创建方法 resume() - 创建线程对象并启动它 - 和 pause() - 杀死线程(参见 Lunarlander 示例) - 在 SurfaceView 类中,并从 surfaceCreated 和 surfaceDestroyed 调用它们以启动和停止线程。
Now in the Activity that runs the SurfaceView, you will also need to call the resume() and pause() methods in the SurfaceView from the Activity's (or fragment's) onResume() and onPause(). It's not an elegant solution, but it will work.
现在,在运行 SurfaceView 的 Activity 中,您还需要从 Activity(或片段)的 onResume() 和 onPause() 调用 SurfaceView 中的 resume() 和 pause() 方法。这不是一个优雅的解决方案,但它会起作用。
回答by Androidcoder
This bug appears to relate to the lunar lander bug, which is quite famous (do a Google search on it). After all this time, and after several android version releases, the bug still exists and no one has bothered to update it. i have found this to work with the least code clutter:
这个错误似乎与月球着陆器错误有关,这是非常有名的(在谷歌上搜索一下)。过了这么久,又发布了几个android版本,这个bug依然存在,没有人费心去更新。我发现这可以处理最少的代码混乱:
public void surfaceCreated(SurfaceHolder holder) {
if (thread.getState==Thread.State.TERMINATED) {
thread = new MainThread(getHolder(),this);
}
thread.setRunning(true);
thread.start();
}
回答by tjb
The best way I have found is to override the onResume method of the activity controlling the surface view so that with the method it re-instantiates the SurfaceView and then sets it with setContentView. The problem with this approach is that you need to reload any state that your SurfaceView was taking care of.
我发现的最好方法是覆盖控制表面视图的活动的 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));
}
回答by AZ_
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...
}
}
}
回答by Danish Sajwani
This is what I have used. The app does not crashes now.
这是我用过的。该应用程序现在不会崩溃。
View Class:
查看类:
holder.addCallback(new Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.setRunning(false);
gameLoopThread.stop();
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
In the GameLoopThread :
在 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){}
}
}
I hope it will help.
我希望它会有所帮助。
回答by Matt K
Tried to comment on the accepted answer above but couldn't, new to this. I don't think you should be calling your start/stop thread methods from both your SurfaceView and Activity. This will result in starting/stopping the thread doubly, and you can't start a thread more than once. Just call your methods from the Activity's onPause and onResume. They're called when exiting and re-entering the app so this will make sure your states are handled properly. surfaceDestroyed isn't always called, which messed me up for a while.
试图对上面接受的答案发表评论,但不能,这是新的。我认为您不应该从 SurfaceView 和 Activity 调用启动/停止线程方法。这将导致两次启动/停止线程,并且您不能多次启动一个线程。只需从活动的 onPause 和 onResume 调用您的方法。它们在退出和重新进入应用程序时被调用,因此这将确保您的状态得到正确处理。surfaceDestroyed 并不总是被调用,这让我搞砸了一段时间。
If you use this method make sure to check for a valid surface in your run code before working with your canvas, because the Activity will start the thread in onResume before the surface is available.
如果您使用此方法,请确保在使用画布之前检查运行代码中的有效表面,因为活动将在表面可用之前在 onResume 中启动线程。
while (_run) {
if (_surfaceHolder.getSurface().isValid()) {
...
}
} //end _run
回答by fyodorananiev
Another solution for this good-known problem. Sadly, I don't understand why it works -- it came out accidentally. But it works good for me and it's easy to implement: no overriding of Activity
's onPause()
, onResume()
, onStart()
, onStop()
, nor writing of special thread methods (like resume()
, pause()
) are required.
这个众所周知的问题的另一种解决方案。可悲的是,我不明白它为什么起作用——它是意外出现的。但它对我很有用,而且很容易实现:不需要覆盖Activity
's onPause()
, onResume()
, onStart()
, onStop()
,也不需要编写特殊的线程方法(如resume()
, pause()
)。
Special requirement is to put all changing variables in something other than rendering thread class.
特殊要求是将所有变化的变量放在渲染线程类以外的地方。
Main points to add to render-thread class:
添加到渲染线程类的要点:
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
}
}
}
Now, important things about YourAppViewClass
:
现在,重要的事情是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) {
}
}
}
Two code blocks above are not full-written classes, but mere notion of which commands in which methods are needed. Also note that each return to app invokes surfaceChanged()
.
上面的两个代码块不是完整编写的类,而只是需要哪些命令哪些方法的概念。另请注意,每次返回应用程序都会调用surfaceChanged()
.
Sorry for such space-consuming answer. I hope it will work properly and will help.
抱歉,回答如此费空间。我希望它能够正常工作并有所帮助。
回答by Moncader
You should use the Activities onPause() and onResume() methods.
您应该使用活动 onPause() 和 onResume() 方法。
First, in surfaceCreated(), start the thread. Also, in onResume(), make sure the thread isn't already started (keep a variable inside the thread or something). Then if it is not running, set it as running again. in onPause(), pause the thread. In surfaceDestroyed, pause the thread again.
首先,在surfaceCreated() 中,启动线程。此外,在 onResume() 中,确保线程尚未启动(在线程内保留一个变量或其他东西)。然后,如果它没有运行,请将其设置为再次运行。在 onPause() 中,暂停线程。在surfaceDestroyed中,再次暂停线程。