Home > Software engineering >  Why does my Android studio App crash when I spam click at the beginning of an activity?
Why does my Android studio App crash when I spam click at the beginning of an activity?

Time:07-23

The app works fine if I wait a bit after clicking play, but if I want click on anything clickable right away, the app crashes.

Here is the error message:

E/InputEventReceiver: Exception dispatching input event.
D/AndroidRuntime: Shutting down VM E/AndroidRuntime: FATAL EXCEPTION:
main
    Process: com.example.combatcats, PID: 13769
    java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
        at com.example.combatcats.Joystick.getIsPressed(Joystick.java:86)
        at com.example.combatcats.GameView.onTouchEvent(GameView.java:187)
        at android.view.View.dispatchTouchEvent(View.java:14540)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:502)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1890)
        at android.app.Activity.dispatchTouchEvent(Activity.java:4196)
        at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:460)
        at android.view.View.dispatchPointerEvent(View.java:14799)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:6347)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6148)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5626)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5683)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5649)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5814)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5657)
        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5871)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5630)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5683)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5649)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5657)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5630)
        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:8562)
        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:8513)
        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:8482)
        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:8685)
        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:259)
        at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native
Method)
        at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:239)
        at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:8642)
        at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:8771)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1037)
        at android.view.Choreographer.doCallbacks(Choreographer.java:845)
        at android.view.Choreographer.doFrame(Choreographer.java:772)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7839)
        at java.lang.reflect.Method.invoke(Native Method) E/AndroidRuntime:     at
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Here is my GameView class:

package com.example.combatcats;

import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceView;


public class GameView extends SurfaceView implements Runnable {

    private final Joystick joystick;
    private Thread thread;
    private boolean isPlaying;
    private int screenX, screenY;
    public static float screenRatioX, screenRatioY;
    private Paint paint;
    private Cat cat;
    private Background background1, background2;
    private int InAirCounter;
    private final MainMenuButton mainMenuButton;
    private GameScreen activity;

    public GameView(GameScreen activity, int screenX, int screenY) {
        super(activity);

        this.activity = activity;

        this.screenX = screenX;
        this.screenY = screenY;
        screenRatioX = 1920f / screenX;
        screenRatioY = 1080f / screenY;


        background1 = new Background(screenX, screenY, getResources());
        background2 = new Background(screenX, screenY, getResources());

        cat = new Cat(screenY, getResources());

        background2.x = screenX;

        paint = new Paint();

        joystick = new Joystick(190,900,90,55);

        mainMenuButton = new MainMenuButton(60,60,250,158);
    }

    @Override
    public void run() {

        while (isPlaying) {

            update ();
            draw ();
            sleep ();
        }
    }

    private void update () {

        background1.x -= 10 * screenRatioX;
        background2.x -= 10 * screenRatioX;



        InAirCounter -= 1;

        if (background1.x   background1.background.getWidth() < 0) {
            background1.x = screenX;
        }
        if (background2.x   background2.background.getWidth() < 0) {
            background2.x = screenX;
        }

        if (cat.y == (screenY - cat.height) && cat.isJumping) {
            InAirCounter = 7;
        }

        if (InAirCounter > 0)
            cat.y -= 30 * screenRatioY;
        else
            cat.y  = 30 * screenRatioY;

        if (cat.y < 0)
            cat.y = 0;
        if (cat.y > screenY - cat.height)
            cat.y = screenY - cat.height;
        if (cat.x < 0)
            cat.x = 0;
        if (cat.x > screenX - cat.width)
            cat.x = screenX - cat.width;

        //Log.d("","var_name = " var);

        cat.update(joystick);

        joystick.update();
    }

    private void draw () {

        if (getHolder().getSurface().isValid()) {

            Canvas canvas = getHolder().lockCanvas();
            canvas.drawBitmap(background1.background, background1.x, background1.y, paint);
            canvas.drawBitmap(background2.background, background2.x, background2.y, paint);


            canvas.drawBitmap(cat.getBlink(), cat.x, cat.y, paint);
            joystick.draw(canvas);




            mainMenuButton.draw(canvas);


            getHolder().unlockCanvasAndPost(canvas);


        }

    }

    private void sleep () {
        try {
            Thread.sleep(17);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void resume () {

        isPlaying = true;
        thread = new Thread(this);
        thread.start();

    }

    public void pause () {

        try {
            isPlaying = false;
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (event.getX() > screenX / 2 && event.getY() > screenY / 2) {
                    cat.isJumping = true;
                }
                if(joystick.isPressed((double) event.getX(), (double) event.getY())){
                    joystick.setIsPressed(true);
                }

                if(event.getX() >= mainMenuButton.getValue(1) &&
                        event.getX() <= mainMenuButton.getValue(3) &&
                            event.getY() >= mainMenuButton.getValue(2) &&
                                event.getY() <= mainMenuButton.getValue(4)) {
                    isPlaying = false;
                    activity.startActivity(new Intent(activity, MainActivity.class));
                    activity.finish();

                }
                break;

            case MotionEvent.ACTION_MOVE:
                if(joystick.getIsPressed()) {
                    joystick.setActuator((double) event.getX(), (double) event.getY());
                }
                break;

            case MotionEvent.ACTION_UP:
                joystick.setIsPressed(false);
                joystick.resetActuator();
                cat.isJumping = false;
                break;
        }
        return true;
    }
}

CodePudding user response:

The call stack

virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference at com.example.combatcats.Joystick.getIsPressed(Joystick.java:86) at com.example.combatcats.GameView.onTouchEvent(GameView.java:187) at

tells you that in the method Joystick.getIsPressed you are trying to convert a Boolean object to a boolean by invoking booleanValue() on it, but that object is null.

You didn't show the code for Joystick but I suspect it looks something like this:

class Joystick {
    Boolean pressed;

    void setIsPressed(boolean pressed){
       this.pressed = pressed;
    }

    boolean isPressed() {
        return pressed.getBooleanValue();
    }

The important part is that pressed doesn't get initialised on construction.

If we inspect your GameView.onTouchEvent method you'll note that setIsPressed is invoked on ACTION_DOWN and ACTION_UP, while isPressed is invoked on ACTION_MOVE.

When you wait a little until the UI is fully initialized ACTION_MOVE will probably always happen after ACTION_DOWN and therefore everything is fine. But when spam clicking it might happen that the ACTION_DOWN event doesn't get forwarded to your view, because it is not ready yet. But ACTION_MOVEis. Now this is the first event you process,pressed` is not yet initialized and you get the NPE you are seeing.

The solution is to either initialize pressed on construction of Joystick or even better: make it a primitive boolean in the first place.

  • Related