结束SurfaceView和GameThread以退出应用程序

6
我有一个SurfaceView和GameThread类。GameThread一直在SurfaceView类上更新和绘制。
现在,当我退出应用程序(通过按下Home或Back按钮)时,我会收到一个强制关闭应用程序的消息。这是因为GameThread仍然试图在已擦除的SurfaceView上绘制...
那么,我如何正确地结束应用程序而不会收到此强制关闭通知?我希望在按下后退按钮时GameThread类停止。在按下Home并在后台运行时,它应该暂停。重新进入仍在运行的游戏时,它应该恢复...
有什么想法吗?
这是我的Gamethread类:
public class GameThread extends Thread{

private GameView view;
public boolean isRunning = false;

public GameThread(GameView view) {
    this.view = view;
}

public void setRunning(boolean setRunning) {
    isRunning = setRunning;
}

public void run() {
    while(isRunning) {
        Canvas c = null;
        view.update();
        try {
            c = view.getHolder().lockCanvas();
            synchronized (view.getHolder()) {
                view.draw(c);
            }
        }finally {
            if(c != null) {
                view.getHolder().unlockCanvasAndPost(c);
            }
        }
    }
}

它不断更新我的GameView类:
公共类GameView扩展SurfaceView{
private GameThread gameThread;

public GameView(Context context, Activity activity) {
    super(context);
    gameThread = new GameThread(this);
    init();
    holder = getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            gameThread.setRunning(true);
            gameThread.start();

        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
        }
    });
}


public void init() {

}

public void update() {

}

public void draw(Canvas canvas) {
    super.draw(canvas);

}
}

当按下Home键时,我的Logcat会显示如下信息:

02-24 18:24:59.336: E/SurfaceHolder(839): Exception locking surface
02-24 18:24:59.336: E/SurfaceHolder(839): java.lang.IllegalStateException: Surface has already been released.
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.Surface.checkNotReleasedLocked(Surface.java:437)
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.Surface.lockCanvas(Surface.java:245)
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:872)

主要活动:

public class MainActivity extends Activity {

private RelativeLayout relativeLayout;
private GameView gameView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    gameView = new GameView(this, this);
    setContentView(R.layout.activity_main);
    relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
    relativeLayout.addView(gameView);
}

@Override
public void onBackPressed() {
    // TODO Auto-generated method stub
    super.onBackPressed();
    gameView.onBackPressed();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}

请添加主活动代码! - BlueSword
@Jay 添加了主活动代码。 - user2410644
2个回答

2

你可以像这样更改你的代码。

GameView

@Override
    public void surfaceCreated(SurfaceHolder holder) {
        MainActivity obj=new MainActivity();
        stop=obj.getBoolean(); //receive status of boolean from main activity

        //stop is boolean set if backPressed in main activity
        if(!stop){ 
        gameThread.setRunning(true);
        gameThread.start();
        }
        else
            gameThread.setRunning(false);
    }

MainActivity中。
public Boolean getBoolean(){
return stop;    
} 

public void setBoolean(Boolean bools){
stop=bools;
}

@Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();

stop=true;
setBoolean(stop);

try{
Thread.sleep(200); // Be safeside and wait for Game thread to finish
}
catch(Exception e){
e.printStackTrace();
}

MainActivity.finish();
}

在执行返回操作之前,请先调用setBoolean方法,否则会出现错误。根据您的需求对代码进行修改。

希望这有所帮助。祝好运。 :)


我不确定你为什么要在 onBackPressed() 上进行陷阱处理 - 有许多事件会导致 Activity 暂停。更好的方法是重写生命周期方法(例如 onPause()),如果您正在尝试优化旋转,则检查 isFinishing() 的值。如果您将使用在线程之间通信的布尔值 "stop",请确保将其声明为 volatile。如果您需要等待某些内容停止,请使用显式的 wait/notify 而不是 sleep,否则您将会从设备中获得偶尔难以调试的故障。 - fadden

2
在应用程序生命周期的onPause()onDestroy()之间,SurfaceView底层的Surface会被销毁。在onPause()中向您的渲染线程发送消息,并等待该线程关闭。

例如,请参阅Grafika中的“Hardware scaler exerciser”活动。它在SurfaceHoldersurfaceCreated()回调中启动了渲染线程。

更新:surfaceCreated()中启动线程然后在onPause()中停止线程感觉很奇怪,因此在稍作整理后,HardwareScalerActivity现在在surfaceDestroyed()中停止线程。(SurfaceView的文档有点模糊 - 它说Surface在“surfaceCreated()surfaceDestroyed()”之间有效 - 但是查看SurfaceView实现,这似乎是预期的用法)。

更新2:在发现无法处理的情况后,我进一步更新了代码。源文件中现在有一个60行的超级注释,也可以看到我回答类似问题的答案

更新3:超级注释变成了架构文档附录。Grafika的“hardware scaler exerciser”活动演示了方法2,而“texture from camera”则演示了方法1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接