java - Android Activity 在切换应用程序时崩溃

标签 java android multithreading

将 Android Activity 推送到后台(按下应用程序切换器或主页按钮)时,应用程序会立即崩溃(“不幸的是,应用程序已停止。”)。

我的 Activity 如下所示:

public class MyActivity extends Activity {
    MyView myView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
    } 
}

有问题的 View 使用线程进行绘图:

public class MyView extends SurfaceView implements SurfaceHolder.Callback
{
    protected Engine engine;

    protected SurfaceHolder surfaceHolder;
    protected Context context;

    private PaintThread thread;

    void initView() {
        // Initialize our screen holder
        SurfaceHolder holder = getHolder();
        holder.addCallback( this);

        // Get screen size
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

        // Initialize engine
        engine = new Engine(displayMetrics.widthPixels, displayMetrics.heightPixels);
        engine.init(context);

        thread = new PaintThread(holder, context, new Handler(), engine);
        setFocusable(true);
    }

    public MyView(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        this.context = context;
        initView();
    }

    public MyView(Context context, AttributeSet attrs){
        super(context, attrs);
        this.context = context;
        initView();
    }

    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {}

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        Log.i("app", "here!");
        boolean retry = true;

        thread.state = PaintThread.PAUSED;
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {}
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        if (thread.state == PaintThread.PAUSED){
            thread = new PaintThread(getHolder(), context, new Handler(), engine);
            thread.start();
        } else {
            thread.start();
        }
    }
}

线程是这样工作的:

public class PaintThread extends Thread {
    private SurfaceHolder surfaceHolder;
    private Handler handler;
    private Context context;

    private Engine engine;

    public int state = 1;

    public final static int RUNNING = 1;
    public final static int PAUSED = 2;

    public PaintThread(SurfaceHolder surfaceHolder, Context context, Handler handler,
                       Engine engine) {
        this.surfaceHolder = surfaceHolder;
        this.handler = handler;
        this.context = context;

        this.engine = engine;
    }

    @Override
    public void run() {
        long previousUpdate = System.nanoTime();
        long beforeTime;
        long passedTime;
        long accumulator = 0;
        long dt = 1000/60;

        while (state == RUNNING) {
            beforeTime = System.nanoTime();
            passedTime = TimeUnit.MILLISECONDS.convert(beforeTime - previousUpdate, TimeUnit.NANOSECONDS);
            previousUpdate = beforeTime;

            accumulator += passedTime;

            while (accumulator >= dt) {
                engine.update(dt);
                accumulator -= dt;
            }

            Canvas c = null;
            try {
                c = surfaceHolder.lockCanvas(null);

                synchronized (surfaceHolder) {
                    c.drawColor(Color.WHITE);
                    engine.draw(c);
                }
            } finally {
                if (c != null) {
                    surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

当按下其中一个按钮时,将显示其 surfaceDestroyed 方法的 View 中的日志消息,但应用程序仍会崩溃并显示以下消息:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int)' on a null object reference

受影响的行来自 PaintThread 它的 run 方法:

c.drawColor(Color.WHITE);

似乎 lockCanvas 在那个时候没有返回我们可以使用的 Canvas。这似乎是预期的行为,因为当按下其中一个按钮时, Canvas 将置于背景中。

但是,这一切都发生在线程暂停之前刚好。发生这种情况的设置有什么问题或如何防止这种情况发生?

最佳答案

在我们使用它之前检查 c 是否为 null 的 if 语句?

关于java - Android Activity 在切换应用程序时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29578274/

相关文章:

java - 根据 Java 中的语法验证字符串

android - 为什么 android 不允许我在我自己的线程中更新我的 UI

java - 等待直到在另一个线程中获取锁

java - Android Native C++,使用线程

android - 将在 onStop 之后调用 onCreate 而中间没有 onDestroy

Java CMS GC : can a minor GC occur between final remark and concurrent sweep?

java - JPA/Hibernate 实体类和同步的最佳实践是什么?

android - 如何解决可恢复的协议(protocol)解码器异常:the line is too long in android

java - 从 fragment 更改 Activity 工具栏标题

java - 为什么我们在启动 jvm 时指定最小和最大堆内存