【问题标题】:End SurfaceView and GameThread on exiting app在退出应用程序时结束 SurfaceView 和 GameThread
【发布时间】:2014-03-26 12:11:08
【问题描述】:

我有一个surfaceview 和一个gameThread 类。 gameThread 在 SurfaceView 类上不断更新和绘画。

现在,当我退出应用程序(通过按主页或返回按钮)时,我会收到一条应用程序强制关闭的消息。那是因为 GameThread 仍然尝试在已擦除的表面视图上绘图...

那么如何在不收到强制关闭通知的情况下正确结束应用程序?我希望 GameThread 类在按下后退按钮时停止。当按下主页并在后台运行时,它应该暂停。当重新进入仍在运行的游戏时,它应该会恢复....

有什么想法吗?

这是我的 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;
}

}

【问题讨论】:

  • 请添加主活动代码!
  • @Jay 添加主要活动代码

标签: android multithreading surfaceview exit


【解决方案1】:

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

有关示例,请参阅Grafika 中的“硬件缩放器练习器”活动。它在SurfaceHoldersurfaceCreated() 回调中启动渲染线程。

更新:surfaceCreated() 中启动线程并在onPause() 中停止它感觉很奇怪,所以经过一些重新洗牌后,HardwareScalerActivity 现在停止在surfaceDestroyed() 中的线程。 (SurfaceView 文档有点模棱两可——它说 Surface 在“surfaceCreated()surfaceDestroyed()”之间是有效的——但看看 SurfaceView 的实现,这似乎是预期的用法。)

更新 2: 在发现代码处理不好的情况后,我又更新了一些代码。源中现在有一个 60 行的巨型评论,在 my answer to a similar question 中也可以看到。

更新 3: 超级评论变成了architecture doc appendix。 Grafika 的“硬件缩放器练习器”活动演示了方法 2,而“来自相机的纹理”演示了方法 1。

【讨论】:

    【解决方案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();
    }
    

    在 backPress 之前调用 setBoolean 一次,否则会出错。根据您的需要对代码进行修改。

    希望对您有所帮助。干杯。 :)

    【讨论】:

    • 我不知道你为什么要困在onBackPressed() 上——有许多事件会导致 Activity 暂停。最好覆盖生命周期方法(例如onPause()),如果您尝试优化旋转,请检查isFinishing() 的值。如果您要使用在线程之间通信的布尔“停止”值,请确保将其声明为volatile。如果您需要等待某事停止,请使用显式等待/通知而不是睡眠,否则您会遇到现场设备偶尔难以调试的故障。
    猜你喜欢
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多