javaandroidxmlandroid-layoutactivity-lifecycle

button.setOnClickListener() Crashes Android App, on Device Rotation


I'm working on a project that updates a textview with text when a button is pressed. For example, you press 0 and 0 displays on the textview. When the device changes orientation, such as when the user rotates their device, the application crashes.

When I comment out the button0.setOnClickListener(new Buttons(request, "0")); line the application does not crash.

I have verified that there is a Button with the id "zero" in both the portrait and landscape layouts.

The layouts are stored in:

res/layout/activity_main.xml
res/layout-land/activity_main.xml

There are no warnings or error messages in either layout and (again) both layouts function when the ".setOnclickListener()" line of code is not included in the "MainActivity".

First Class. MainActivity.java:

package com.example.myapp;

import android.content.res.Configuration;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    Button button0;
    
    TextView request;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);

        request = findViewById(R.id.request);

        // Number Buttons
        button0 = findViewById(R.id.zero);
        button0.setOnClickListener(new Buttons(request, "0"));

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
    }

}


Second Class. Buttons.java:


package com.example.myapp;

import android.view.View;
import android.widget.TextView;

import static androidx.core.content.res.TypedArrayUtils.getText;

public class Buttons implements View.OnClickListener {

    TextView tv;
    String text;


    public Buttons(TextView tv, String input){
        this.tv = tv;
        this.text = input;
    }

    public Buttons(TextView tv){
        this.tv = tv;
        this.text = "";
    }


    @Override
    public void onClick(View view) {
        if (tv.getText().toString().equals("Request")){
            tv.setText(text);
        }
        else {
            String response = tv.getText().toString() + text;
            tv.setText(response);
        }
    }
}

I've tried attaching this to the MainActivity.java class:

   @Override
    protected void onDestroy() {
      
        button0.setOnClickListener(null);

        super.onDestroy();}

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

Here is the Logcat:

FATAL EXCEPTION: main
    Process: com.example.texascal, PID: 9311
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.texascal/com.example.texascal.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4164)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4322)
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:6499)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:6378)
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:76)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2685)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:230)
        at android.os.Looper.loop(Looper.java:319)
        at android.app.ActivityThread.main(ActivityThread.java:8919)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        at com.example.texascal.MainActivity.onCreate(MainActivity.java:82)
        at android.app.Activity.performCreate(Activity.java:8975)
        at android.app.Activity.performCreate(Activity.java:8944)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4146)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4322) 
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:6499) 
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:6378) 
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:76) 
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2685) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:230) 
        at android.os.Looper.loop(Looper.java:319) 
        at android.app.ActivityThread.main(ActivityThread.java:8919) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) 
2024-07-23 15:21:58.653  9311-9311  Process                 com.example.texascal                 I  Sending signal. PID: 9311 SIG: 9
---------------------------- PROCESS ENDED (9311) for package com.example.texascal ----------------------------



Solution

  • I identified the problem. After learning how to use logcat, it was revealed that I had duplicate layouts for various device size to adapt or respond to different android device form-factors. While a Button with the id zero existed in several of the different layouts it did not exist for all of the layouts. The solution was to verify the 0 button had an id for every layout.

    <Button
        ...
        android:text="0"
        android:id="@+id/zero"/>