我的安卓应用程序的gameLoop线程在退出时崩溃

3
问题在于当我在游戏中点击主页按钮时,gameLoop线程(我想是)出现了问题,然后弹出“很抱歉,‘应用程序名称’已停止运行”的提示。
我制作了一个非常简单的程序,在这个程序中会出现这个问题。所有这个程序要做的就是在屏幕上显示一个数字,并在触摸屏幕时增加该数字。 当我在GameLoopThread中注释掉view.onDraw(c)时,一切似乎都能正常运行。
错误消息:
FATAL EXEPTION: Thread-23207
java.lang.NullPointerExeption
at com.example.crashtest.GameView.onDraw(GameView.java:61)
at com.example.crashtest.GameLoopThread.run(GameLoopThread.java:34)

以下是代码:

MainActivity.java

package com.example.crashtest;

import android.os.Bundle;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.KeyEvent;
import android.view.Window;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(new GameView(this));
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                return true;

            case KeyEvent.KEYCODE_MENU:
                return true;

            case KeyEvent.KEYCODE_VOLUME_UP:
                return true;

            case KeyEvent.KEYCODE_VOLUME_DOWN:
                return true;

            default:
                return false;
        }
    }
}

GameView.java

package com.example.crashtest;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class GameView extends SurfaceView { 
    private SurfaceHolder holder;
    private GameLoopThread gameLoopThread;

    private int num = 0;

    public GameView(final Context context) {
        super(context);

        holder = getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

                   boolean retry = true;
                   gameLoopThread.setRunning(false);
                   while (retry) {
                          try {
                                gameLoopThread.join();
                                retry = false;
                          } catch (InterruptedException e) {}
                   }
            }

            @Override
            public void surfaceCreated(SurfaceHolder holder) {

                makeThread();
                gameLoopThread.start();
                gameLoopThread.setRunning(true);
            }

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

            }
        });
    }

    private void makeThread() {
        gameLoopThread = new GameLoopThread(this);
    }

    @SuppressLint({ "WrongCall", "DrawAllocation" })
    @Override
    protected void onDraw(Canvas canvas) {
        // Draw black background - Write variable 'num' on the screen
        canvas.drawColor(Color.BLACK);

        Paint paint = new Paint();
        paint.setARGB(255, 0, 255, 0);
        paint.setTextSize(50);
        paint.setTextAlign(Align.CENTER);

        canvas.drawText(Integer.toString(num), getWidth() / 2, getHeight() / 2, paint);
    }

    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN) {
            // Increase 'num' with 1 every touch
            num++;
        }

        return super.onTouchEvent(event);
    }
}

GameLoopThread.java

package com.example.crashtest;

import android.annotation.SuppressLint;
import android.graphics.Canvas;

public class GameLoopThread extends Thread {
      static final long FPS = 10;
       private GameView view;
       public boolean running = false;
       public boolean pause = false;

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

       public void setRunning(boolean run) {
             running = run;
       }

       @SuppressLint("WrongCall")
       @Override
       public void run() {
           long ticksPS = 1000 / FPS;
           long startTime = 0;
           long sleepTime = 0;

             while (running) {
                    Canvas c = null;
                    startTime = System.currentTimeMillis();

                    try {
                           c = view.getHolder().lockCanvas();
                           synchronized (view.getHolder()) {
                               view.onDraw(c);
                           }
                    } finally {
                           if (c != null) {
                                  view.getHolder().unlockCanvasAndPost(c);
                           }
                    }

                    sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
                    try {
                           if (sleepTime > 0)
                                  sleep(sleepTime);
                           else
                                  sleep(10);
                    } catch (Exception e) {}
             }
       }
}

1
你能发布异常信息吗? - mohammed momn
既然你只是想停止线程,并且已经让它休眠,为什么不直接中断它呢?另外,为什么要在UI线程上调用“join”(或者至少看起来是这样)? - android developer
@ Mohammed momn:我已经更新了我的帖子。感谢您的回复。 - CandyCreep
@安卓开发者:我很久以前遵循了一份旧指南,所以我不确定我为什么要这样做。我对Java或安卓开发并不是很有经验,所以你能否解释一下我应该如何进行你建议的更改?非常感谢你的回复。 - CandyCreep
1个回答

4

当您关闭 Activity 时,需要注意停止之前所实现的我的代码中的线程:

GameView gameView ;
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        gameView = new GameView(this); 
        setContentView(gameView);
    }

 @Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();
    gameView.StopView();

}

您需要在GameView中实现名为StopView()的方法,示例如下:

 public void StopView() {

    if (gameLoopThread != null) {
        gameLoopThread.setRunning(false);

    }
}

问题在于你在尝试阻塞线程后仍然调用了线程中的run方法,因此在阻塞线程之前需要先停止它,使用join方法。我使用新实现测试了你的代码,表现良好,请回馈意见。


非常感谢!当我有足够的声望时,我也会投票支持你的回答。我真的很感激你的时间和帮助。 - CandyCreep

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