在Android中结束线程 - 剧烈的问题

时间:2013-01-27 15:11:44

标签: android multithreading surfaceview forceclose

我仍然对在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输出。

enter image description here

我的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的行!

enter image description here

由于

编辑:

好的,我已经从头开始并重新编写了全班 - 这是我之前所拥有的精简版本,这里是整个班级:

    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);                     

            }
        }





          }
         }
        }
}

1 个答案:

答案 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并且线程终止。

只要m_bThread为true,

线程就会继续,

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

m_hThread只是代码中的preThread,而m_Thread也是m_hThread,只是输入错误。