我仍然对在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但是当我重新进入活动时,它又说线程已经存在。
*)如果我按下主页键,则运行surfaceDestroyed,当我返回活动时,它表示该线程先前被销毁并开始新的。
*)如果我使用DDMS终止活动,则不运行surfaceDestroyed,当我返回活动时,它表示该线程已经存在。
如果我正在思考,那么第三种情况是唯一似乎有意义的情景。
我显然做了一些严重错误的事情。主要问题是:
如果我在游戏过程中点击主页键,然后在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);
}
}
}
}
}
}
答案 0 :(得分:1)
线程很难管理,但经过一些打击和试用后,我想我已经想出了一个大部分时间都能正常工作的方案。 结束线程
if(m_hThread != null)
{
try
{
m_bThread = false; // m_bThread is the while condition of the thread
m_hThread.interrupt(); // incase the thread is in sleep
m_Thread.join(); // This call blocks and waits for thread to end
m_hThread = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
用于重新创建线程
if(m_hThread == null)
{
m_bThread = true; //while condition of thread
StartNewThread();
}
在您的实现中,无需重试Thread.join,它将在第一次尝试时加入,否则它将阻塞直到线程加入。至于你的情况,只有第一种情况似乎很奇怪,你发现线程已经在运行,这可能不是真的。第二和第三对我来说是完全合理的,并且应该像他们一样工作。当用户单击Home按钮时,调用surfaceDestroyed
并且线程终止。
线程就会继续,
while(m_bThread) //b
{
// Continuous Thread operations...
}
m_hThread只是代码中的preThread,而m_Thread也是m_hThread,只是输入错误。