Android canvas 游戏循环在关闭并重新打开应用程序后中断 onResume()

Android canvas game loop breaks onResume() after closing and reopening app

在全新安装中,我第一次打开该应用程序时,确实没有遇到任何问题。但是,如果我要通过进程终止应用程序、强制停止或简单地按下主页按钮并重新启动应用程序,我 运行 会遇到一些问题...

第一个问题 - 当应用程序在关闭或终止后重新启动时,在实际启动应用程序之前有很长的延迟window。此外,有时显示游戏图形(绘制到 canvas 的内容)最多需要 30 秒。当我听到游戏音效在后台播放时,我只会看到黑屏,就好像 运行ning。

第二个问题 - 在等待应用程序实际显示 canvas 大约 30 秒后,有时触摸侦听器会取消注册或发生其他问题。我无法与游戏互动,就好像我的触摸没有注册一样。再一次,这只会在应用程序成功 运行 一次然后关闭/终止后重新启动时发生。

第三个问题 - 如果我不耐烦并且不等待canvas显示30秒,而是点击主页按钮或杀死"unresponsive" 应用程序,我总是收到一条错误消息,说 "app unresponsive, wait? force close?" 或 "Unexpected error, App has stop running."

所以这些问题让我相信这是我如何开始/重新启动/创建我的游戏循环的问题:

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 改成 sleep only 1ms,不再断断续续,仍然无法重现问题。 1ms 似乎有点微不足道......为什么线程休眠一小段时间是必要的?

我自己的基于 SurfaceView 的应用程序有一个单独的线程,其中有两个方法 运行:update() 和 draw()。但是,draw() 方法只有 运行s if 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);
            }
        }
    }
}

希望这对您有意义!您应该能够根据需要将其适应您自己的项目。

请记住,SurfaceView 有两个部分,Surface 和 View。如果您在 SurfaceView sub-class 中重写 onDraw(),您将同时执行这两项操作,并且无论您在 View 上绘制什么,都会掩盖您在 Surface 上绘制的内容。 (Surface 是一个独立层,默认情况下位于 View UI 层之后。)

我通常建议不要子 classing SurfaceView,因为这样做没有任何价值,而且它可能会产生怪异的可能性。 SurfaceView Surface 是您在其上绘制的东西,而不是您需要专门化其行为的对象。 (对比一个custom View。)

您可能需要阅读有关 SurfaceView and Activity lifecycle interaction for the pause/resume/thread logic, and game loops for some ideas about how best to structure game rendering. These point to Grafika 的具体示例。

添加睡眠调用会影响竞争条件。如果您的应用没有比赛,则无需睡眠。 (而且,正如游戏循环文章中所述,它们并不是管理帧速率的好方法。)

小点:ok应该标记为volatile,否则在run().

中允许编译器忽略它