对于何时/如何结束 Surfaceview 应用程序中的线程,我仍然很困惑,希望有人能解释一下。
目前,我正在使用以下代码:
Log.v("Destroyed","Surface Destroyed");
preThread.setRunning(false);
boolean retry = true;
while (retry) {
try {
preThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
上面的代码位于我的 surfaceDestroyed 方法中 - 首先,这是正确的吗?
在我的 surfaceCreated 方法中,我有以下代码应该检查线程是否仍然存在或已停止,如果已停止,则重新启动它:
if (runthread==false){
if (preThread.getState()==Thread.State.TERMINATED){
preThread = new MainThread(thisholder, thiscontext, thishandler);}
else {}
preThread.setRunning(true);
preThread.start();
}
它似乎表现得很奇怪。这是我得到的:
*)当我第一次安装游戏并运行它时,通过我的日志,它说线程已经存在,如果我然后按返回键,surfaceDestroyed 运行但是当我回到活动时,它又说线程已经存在。
*) 如果我按下 home 键,然后 surfaceDestroyed 运行,当我返回活动时,它说线程先前已被销毁并启动一个新线程。
*) 如果我使用 DDMS 终止活动,surfaceDestroyed 不会运行,当我返回活动时,它说线程已经存在。
如果我想清楚,那么第三种情况似乎是唯一有意义的。
我显然做错了什么。主要问题是这样的:
如果我在游戏过程中按了 home 键,然后在 Eclipse 中通过 DDMS 结束应用程序,重新启动应用程序并快速连续按两次返回键(一次,返回上一个活动,然后再次返回启动画面屏幕) - 应用程序强制关闭,我在 logcat 中收到“致命异常:线程 12”。我不得不假设这是因为我的线程永无止境并且正在尝试重新启动?我不知道。
我一直试图弄清楚这个似乎是一个年龄,所以我真的希望有人能解释我做错了什么!
非常感谢!
编辑。Logcat 输出。
我的 Run() 方法:
public void run(){
//Main Loop
while (runthread){
Log.v("tracking","runthread is: "+runthread); //This should only be logged while this loop is running
timestart = System.currentTimeMillis(); //Get time at start of loop for FPS calc
try{c=mySurfaceHolder.lockCanvas(); //Set Canvas to locked
synchronized(mySurfaceHolder){
if (c==null){Log.v("Stop","Canvas is null for some reason - exiting, "+c+" - see?!!!");}
framesskipped = 0; // resetting frames skipped
doDraw(c); //Draw to the screen
updateMenu();
}
}
finally{
if (c != null){
mySurfaceHolder.unlockCanvasAndPost(c); //Post canvas
}
}
//work out timings
timeend = System.currentTimeMillis(); //get end time for current frame (for FPS)
frametime = timeend-timestart; //Set the frametime variable to the time the frame took to render & update (end time - start time)
sleepfor = (int) (33-frametime); // this is the time that the thread will sleep for if <target time
if (sleepfor>0){ // If the 'sleepfor' variable is >0 then set the thread to sleep for it's value (expressed in ms)
try {
OptionsThread.sleep(sleepfor); //send thread to sleep for value of sleepfor (determined above).
} catch (InterruptedException e) {} //in case of exception
} //close if statement
while (sleepfor<0 && framesskipped<maxframesskipped){ //if sleepfor is < 0 (ie, frame took longer to render than target time and the maxframesskipped has not reached it's limit)
updateMenu(); //Update animation variables without rendering to the screen while these conditions are met
sleepfor+=33; //time to sleep plus the time frame took to render
framesskipped++; //add one to framesskipped variable so this only skips a certain number of frames
}
}
}
新的 Logcat 输出显示 nullPointerException 和日志记录输出。runThread 永远不会被记录为 false,所以我不确定如何到达将 canvas 记录为 null 的行!
谢谢
编辑:
好的,我已经完全从头开始并重新编写了整个课程——这是我之前所拥有的非常精简的版本,这里是整个课程:
import android.content.res.Resources;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class OptionsScreen extends SurfaceView implements
SurfaceHolder.Callback {
//Create Variables
private SurfaceHolder thisHolder;
private Context thisContext;
private Handler thisHandler;
private preThread thread;
private Bitmap background;
private Resources res;
private Context myContext;
private Handler myHandler;
private Canvas c;
// thisholder = getHolder();
public OptionsScreen(Context context) {
super(context);
myContext=context; //This is the context passed into this constructor (this)
thisHolder = getHolder(); //Get surface holder
thisHandler=getHandler(); //Get Handler
thisContext = getContext(); //Get context
res=getResources(); //Get resource
//add the callback surface holder
getHolder().addCallback(this);
//make focusable
setFocusable(true);
//create new thread
thread = new preThread(thisHolder, thisContext, thisHandler);
//create bitmaps from resources
background = BitmapFactory.decodeResource(res, R.drawable.sky);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.v("check","surfaceChanged run");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("check","surfaceCreated run"+thread.getState());
int height = this.getHeight();
int width = this.getWidth();
if(thread.getState()==Thread.State.TERMINATED){ //Has thread been stopped previously? could happen if the home key is pressed
Log.v("check","Thread still exists!!!! - Starting a new one. "+thread.getState());
thread = new preThread(thisHolder, thisContext, thisHandler);
}
thread.setRunning(true);
thread.start();
Log.v("check","Thread - "+thread.getState());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("check","surfaceDestroyed run"+thread.getState());
thread.setRunning(false); //Set to false to exit run() method
boolean retry = true; //Shut off rendering thread
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.v("check","Surface Touched");
Log.v("check","Thread - "+thread.getState());
// System.exit(0);
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
// if (canvas!=null){
canvas.drawBitmap(background, 0, 0, null);
Log.v("Stop","Canvas is "+canvas);
}
}
//*******************************************************************
//** run loop **
//*******************************************************************
protected class preThread extends Thread {
private SurfaceHolder mySurfaceHolder;
private Context myContext;
public preThread(SurfaceHolder surfaceholder, Context context, Handler handler) { //Constructor
mySurfaceHolder=surfaceholder;
myContext=context;
res = myContext.getResources();
}
// flag
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run() {
while (running) {
try{c=mySurfaceHolder.lockCanvas();
synchronized(mySurfaceHolder){
Log.v("check","Drawing!!");
onDraw(c);
}
}
finally{
if (c != null){
mySurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}