2

对于何时/如何结束 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);                     

            }
        }





          }
         }
        }
}
4

1 回答 1

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,它要么会在第一次尝试中加入,要么会阻塞直到线程加入。至于您的情况,只有第一种情况似乎很奇怪,您发现线程已经在运行,这不可能是真的。第二和第三对我来说完全有意义,并且像他们应该的那样工作。当用户单击主页按钮surfaceDestroyed被调用并且线程被终止。

只要 m_bThread 为真,线程就会继续,

 while(m_bThread) //b
    {
      // Continuous Thread operations...
    }

m_hThread 在您的代码中只是 preThread ,而 m_Thread 也是 m_hThread 只是这里的打字错误。

于 2013-01-31T06:53:36.747 回答