How can I split MainActivity functionality into an another class for clarity?

420 views Asked by At

I have an android program and I want to be able to split the functionality in MainActivity into multiple files to keep my code organized, but I'm getting a null object reference error.

To demonstrate the error, I created a simple program that has only a textView and a button to change the textView. The error occurs when the button is clicked. How can I fix this issue so I can have a helper class?

MainActivity.java

package com.example.testapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    private MainActivityHelper mainActivityHelper;

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

        mainActivityHelper = new MainActivityHelper();
    }

    public void buttonPressed(View view) {
        mainActivityHelper.changeText();
    }
}

MainActivityHelper.java

package com.example.testapp;

import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivityHelper extends AppCompatActivity {
    public MainActivityHelper() {

    }

    public void changeText() {
        TextView textView = findViewById(R.id.helloString);

        if(textView.getText().toString() == "Hello World!") {
            textView.setText("Goodbye world!");
        }
        else {
            textView.setText("Hello World!");
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Error message:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.testapp, PID: 17322
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:389)
        at android.view.View.performClick(View.java:6294)
        at android.view.View$PerformClick.run(View.java:24770)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384)
        at android.view.View.performClick(View.java:6294) 
        at android.view.View$PerformClick.run(View.java:24770) 
        at android.os.Handler.handleCallback(Handler.java:790) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6494) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
        at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:117)
        at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149)
        at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:56)
        at android.support.v7.app.AppCompatDelegateImplV23.<init>(AppCompatDelegateImplV23.java:31)
        at android.support.v7.app.AppCompatDelegateImplN.<init>(AppCompatDelegateImplN.java:31)
        at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:198)
        at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
        at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
        at android.support.v7.app.AppCompatActivity.findViewById(AppCompatActivity.java:190)
        at com.example.testapp.MainActivityHelper.changeText(MainActivityHelper.java:14)
        at com.example.testapp.MainActivity.buttonPressed(MainActivity.java:19)
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384) 
        at android.view.View.performClick(View.java:6294) 
        at android.view.View$PerformClick.run(View.java:24770) 
        at android.os.Handler.handleCallback(Handler.java:790) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6494) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
4

There are 4 answers

0
TomD88 On BEST ANSWER

You have a null pointer because you haven't set the content view for Main activity helper. Use and Activity inside another one is not the correct way to reach your objective.

If You want to have an helper class you don't have to extends an Activity or to declare a field that Is an Activity. The best way Is to use a viewmodel that Is a common backend class used into mvvm pattern. Viewmodels allows the separation of the Logic from the Activity code.

If you want continue your case Just declare a normal Java class and pass a reference of Activity to the constructor of this class. Something likes this in onCreate of Main Activity:

MainActivityHelper = new MainActivityHelper(this);

After that you can call methods of your activity inside the helper (e.g. likes findviewbyid).

Hope this give you some hints/help.

Cheers.

0
juicy_cesar On

You cannot just break an activity that way. I would recommend to use fragments inside activity, that way you could isolate every fragment with specific pieces of code and just make a simple call back to the activity (at this point, the activity is a simple container to glue all the fragments, serves as a mediator).

Please take a look at this Android Fragments.

This one could be outdated but it's a good entrypoint vogella.

1
Oliver George On

You can't split the activity into two classes that extend the AppCompatActivity class, but you can have a helper class or "Utils" class which holds a set of static functions, each one serving a specific purpose. This way you can make the activity code more readable by minimizing it and moving portions of your code to the helper class.

Also, you can use ViewModel with LiveData to separate your data "Fetching" logic and UI logic. You can learn more about this here: https://developer.android.com/jetpack/docs/guide

0
S.R On

you are getting NPE, because you are using findViewById without attaching the content view.

you can create a separate class and put this method and all your other methods inside it:

Class that contains all of your constants

public final class Constants {

    public static final String HELLO_WORLD = "Hello World!";
    public static final String GOODBYE_WORLD = "Goodbye world!";

}

Class that contains all of your methods

public class Methods {

    public static void changeText(TextView textView) {
        if(textView.getText().toString() == Constants.HELLO_WORLD) {
            textView.setText(Constants.GOODBYE_WORLD);
        }
        else {
            textView.setText(Constants.HELLO_WORLD);
        }
    }


    public static void otherMethods() {
        // other methods
    }


}

And use it inside your MainActivity like this

setContentView(R.layout.activity_main);

TextView textView = findViewById(R.id.helloString);
Methods.changeText(textView)

Related Questions in ILLEGALSTATEEXCEPTION