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