简体   繁体   English

Android活动在切换应用程序时崩溃

[英]Android activity crashes on switching app

When pushing the Android activity to the background (pressing the app switcher or home button), the application crashes immediately ("Unfortunately, Apps has stopped."). 将Android活动推到后台(按应用程序切换器或主页按钮)时,应用程序立即崩溃(“不幸的是,应用程序已停止。”)。

My activity looks like this: 我的活动如下所示:

public class MyActivity extends Activity {
    MyView myView;

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

The view in question uses a thread for drawing: 有问题的视图使用线程进行绘制:

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();
        }
    }
}

And the thread works like this: 线程的工作方式如下:

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);
                }
            }
        }
    }
}

The log message in the view its surfaceDestroyed method is being displayed when one of those buttons is pressed but the app crashes anyway with the following message: 当按下其中一个按钮但该应用程序仍然崩溃时,将在视图中显示其surfaceDestroyed方法视图中的日志消息,并显示以下消息:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int)' on a null object reference java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“ void android.graphics.Canvas.drawColor(int)”

The affected line is from the PaintThread its run method: 受影响的行来自PaintThreadrun方法:

c.drawColor(Color.WHITE);

It seems like lockCanvas is at that point not returning a Canvas we can work with. 似乎lockCanvas当时并未返回我们可以使用的Canvas。 This seems to be intended behavior as the canvas is put in the background when one of those buttons is pressed. 这是预期的行为,因为在按下这些按钮之一时将画布置于背景中。

But, this all happens just before the thread is paused. 但是,这一切都发生在线程暂停之前 Is there anything wrong with the setup that this happens or how can this be prevented? 发生这种情况的设置是否有问题,或者如何防止这种情况发生?

一个if语句,在使用c之前检查c是否为null?

I just faced a similar situation to OP, and my fix was to put an If statement around codes that draw on canvas. 我只是遇到了与OP类似的情况,而我的解决方法是在画布上绘制的代码周围放置一个If语句。

 if (canvas != null) {
      // draw on canvas
 }

Hope that helps anyone who reads this! 希望对阅读本文的人有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM