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 が実行されますが、アクティビティに戻ると、再びスレッドが存在すると表示されますもう存在している。

*) ホーム キーを押すと、surfaceDestroyed が実行され、アクティビティに戻ると、スレッドが以前に破棄され、新しいスレッドが開始されたと表示されます。

*) DDMS を使用してアクティビティを強制終了すると、surfaceDestroyed は実行されず、アクティビティに戻ると、スレッドが既に存在すると表示されます。

私の考えが正しければ、3 番目のシナリオだけが理にかなっているように見えます。

私は明らかに何か根本的に間違ったことをしています。主な問題は次のとおりです。

ゲーム中にホーム キーを押して、Eclipse で DDMS を介してアプリを終了した場合、アプリを再起動し、バック キーを 2 回続けて押します (1 回目は前のアクティビティに戻り、次にもう一度スプラッシュに戻ります)。 screen) - アプリが強制終了し、logcat に「致命的な例外: スレッド 12」が表示されます。これは、スレッドが終了せず、再起動しようとしているためだと想定する必要がありますか? わからない。

私は何年もの間これを理解しようとしてきたので、誰かが私が間違っていることを説明できることを本当に願っています!

どうもありがとう!

編集。ログキャット出力。

ここに画像の説明を入力

私の 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
                    }
                }
            }

nullPointerException を示す新しい Logcat 出力とロギングの出力。runThread が false として記録されることはないため、canvas を null として記録する行にどのように到達するのかわかりません!

ここに画像の説明を入力

ありがとう

編集:

OK、私は完全にゼロから始めて、クラス全体を書き直しました。これは、以前に持っていたものの非常に簡素化されたバージョンであり、クラス全体は次のとおりです。

    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 を再試行する必要はありません。最初の試行で参加するか、スレッドが参加するまでブロックします。あなたのケースに関しては、最初のケースだけが奇妙に思えます.スレッドがすでに実行されている場合、これはおそらく真実ではありません. 2 番目と 3 番目は、私にとって完全に理にかなっていて、本来のように機能します。ユーザーがホームボタンをクリックするsurfaceDestroyedと、スレッドが呼び出され、スレッドが終了します。

m_bThread が true である限り、スレッドは継続します。

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

m_hThread は単にコード内の preThread であり、m_Thread も m_hThread であり、ここでの入力ミスです。

于 2013-01-31T06:53:36.747 に答える