在全新安装时,我第一次打开应用程序,我真的没有任何问题。但是,如果我要通过进程终止应用程序,强制停止,或者只需按下主页按钮并重新启动应用程序,我就会遇到一些问题......
第一个问题 - 当应用程序在关闭或被杀后重新启动时,实际启动应用程序窗口之前会有很长的延迟。此外,有时需要最多30秒的时间来显示游戏图形(绘制到画布上的是什么)。当我听到背景中播放的游戏声音效果时,我将会看到一个黑屏,就像它正在运行一样。
第二个问题 - 等待约30秒让应用程序实际显示画布后,有时触摸侦听器会被取消注册或其他内容。我无法与游戏互动,就像我的触摸没有注册一样。再一次,只有在应用程序成功运行一次然后关闭/终止后,才会在RELAUNCH上发生这种情况。
第三个问题 - 如果我不耐烦并且不等待画布显示30秒,而是点击主页按钮或杀死“无响应”的应用程序,我总是得到一条错误消息,上面写着“app unresponsive,wait?force close?”或“意外错误,应用已停止运行。”
所以这些问题让我相信这是我如何开始/重新启动/创建我的游戏循环的问题:
public class MainMenuActivity extends ActionBarActivity implements View.OnTouchListener{
MenuView menu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
menu = new MenuView(this);
menu.setOnTouchListener(this);
setContentView(R.layout.activity_main_menu);
Button buttonplay = (Button)findViewById(R.id.buttonPlay);
buttonplay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
launchCanvas();
}
});
}
public void launchCanvas(){
setContentView(menu)
}
@Override
public boolean onTouch(View v, MotionEvent event) {
////do stuff
}
return true;
}
////////////////////////////////////////inner MenuView class
public class MenuView extends SurfaceView implements Runnable {
Thread t = null;
SurfaceHolder holder;
boolean ok;
Game game;
public MenuView(Context context){
super(context);
game = new Game(this);
holder = getHolder();
}
@SuppressLint("WrongCall")
@Override
public void run() {
while(ok){
if(!holder.getSurface().isValid()){
continue;
}
Canvas c = holder.lockCanvas();
onDraw(c);
holder.unlockCanvasAndPost(c);
}
}
public void onDraw(Canvas canvas){
canvas.drawARGB(255, 1, 5, 29);
game.draw(canvas);
}
public void pause(){
ok = false;
while(true){
try{
t.join();
}catch (InterruptedException e){
e.printStackTrace();
}
break;
}
t=null;
}
public void resume(){
ok = true;
t = new Thread(this);
t.start();
}
}
////////////////////////////////////////////////////end MenuView class`
@Override
protected void onPause() {
super.onPause();
menu.pause();
}
@Override
protected void onResume() {
super.onResume();
menu.resume();
}
}
关于如何修复的任何想法?
更新 - 发现潜在问题 我相信我的问题的根源不是让我的线程睡觉。我在draw()方法中添加了以下try块,该方法在线程循环的每次迭代中调用:
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
虽然现在有些不稳定且不均匀,但我无法重现上述问题。
我刚刚从10ms变为仅睡1ms,并且它不再是波涛汹涌而且仍然无法重现这些问题。 1ms似乎有点微不足道......为什么这个线程需要睡眠时间不大?
答案 0 :(得分:1)
我自己的基于SurfaceView的应用程序有一个单独的Thread,其中运行两个方法:update()和draw()。但是,draw()方法仅在canvas != null
时运行。这是我与暂停和恢复相关的所有代码,除了运行此代码的surfaceCreated(SurfaceHolder sh) {...}
之外:
if(!thread.isAlive()) {
thread.setRunning(true);
thread.start();
}
如果这对您没有帮助,那么我整个框架中的相关部分:
<强> Main.java 强>:
将MainView设置为内容视图(setContentView(mainView)
)
<强> MainView.java 强>:
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "Surface created");
tf = Typeface.create("Roboto", Typeface.BOLD);
recalc=true;
if(!thread.isAlive()) {
thread.setRunning(true);
thread.start();
}
}
<强> MainThread.java 强>:
public class MainThread extends Thread {
// (abridged) Game logic loop
@Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
// update game state
mainView.update();
// render state to the screen
if (canvas != null) mainView.render(canvas); // canvas == null when activity is in background.
// ... my code then handles sleeping, etc. for fixed fps.
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
希望这对你有意义!您应该能够根据需要将其调整到您自己的项目中。
答案 1 :(得分:1)
请记住,SurfaceView有两个部分,Surface和View。如果您在SurfaceView子类中覆盖onDraw()
,那么您将同时执行这两项操作,而在View上绘制的任何内容都会模糊您在Surface上绘制的内容。 (Surface是一个独立的图层,默认情况下位于View UI图层后面。)
我通常建议不要对SurfaceView进行子类化,因为这样做没有任何价值,并且它会产生奇怪的可能性。 SurfaceView Surface是您绘制的东西,而不是您需要专门化其行为的对象。 (与custom View对比。)
您可能希望阅读有关暂停/恢复/线程逻辑的SurfaceView and Activity lifecycle interaction和game loops有关如何最佳地构建游戏渲染的一些想法。这些具体例子指向Grafika。
添加睡眠呼叫会影响竞争条件。如果您的应用没有比赛,则无需睡觉。 (而且,正如游戏循环文章所述,它们并不是管理帧速率的好方法。)
次要问题:ok
应标记为volatile
,或允许编译器在run()
中忽略它。