Search code examples
javaandroidnullpointerexceptionbutterknife

Set text to textview returns null in android code


I am trying to set a text for my textview every time I update the user interface.

public class CourseDetailsFragment extends Fragment {
  private static final String TAG = "CourseDetailsFragment";
  private OnFragmentInteractionListener listener;
  private static CourseDetailsFragment instance;
  protected @Bind(R.id.courseName)TextView courseName;
  Course c;
  View view;


public static CourseDetailsFragment getInstance(){
    if (instance == null) {
        instance = new CourseDetailsFragment();
    }
    return instance;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    instance = this;
}

@Override
public void onActivityCreated(Bundle savedInstance) {
    super.onActivityCreated(savedInstance);
    ((AppCompatActivity)getActivity()).getSupportActionBar().setLogo(R.drawable.ic_overview);
    ((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("Course Detail"); //redundant?

}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onDetach() {
    super.onDetach();
    listener = null;
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        listener = (OnFragmentInteractionListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_course_details, container, false);
    ButterKnife.bind(this, view);
    return view;
}
public void updateUI(Course cData) {
    c = cData
    courseName.setText(cData.getName());
}

}

This where I am trying to set text for my textview. However courseName remains null.

public void updateUI(Course cData) {
    c = cData
    courseName.setText(cData.getName());
}

This is where updateUI gets called.

public void onDataEntry(boolean saved, Fragment fragment, String identifier,     Object T) {
    if (T instanceof Course){
        Course courseData = (Course)T;
        Log.d(TAG, "onDataEntry: " + courseData.getName() + " " + courseData.getGrade());
        CourseDetailsFragment.getInstance().updateUI(courseData);
    }
}  

This is my stacktrace:

02-19 17:31:56.317 4689-4689/hsleiden.nl.barometer E/AndroidRuntime: FATAL EXCEPTION: main
                                                                 Process: hsleiden.nl.barometer, PID: 4689
                                                                 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
                                                                     at hsleiden.nl.barometer.views.fragment.CourseDetailsFragment.updateUI(CourseDetailsFragment.java:101)
                                                                     at hsleiden.nl.barometer.views.activity.BaseActivity.onDataEntry(BaseActivity.java:306)
                                                                     at hsleiden.nl.barometer.adapter.CourseAdapter$1.onClick(CourseAdapter.java:57)
                                                                     at android.view.View.performClick(View.java:5198)
                                                                     at android.view.View$PerformClick.run(View.java:21147)
                                                                     at android.os.Handler.handleCallback(Handler.java:739)
                                                                     at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                     at android.os.Looper.loop(Looper.java:148)
                                                                     at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                     at java.lang.reflect.Method.invoke(Native Method)
                                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Solution

  • The problem is this line:

    CourseDetailsFragment.getInstance().updateUI(courseData);
    

    The thing is, your @butterknife.Bind annotated TextView will remain null until the call to ButterKnife.bind(this, view). To make that call, you need a valid parent of your view hierarchy, which you can obtain no earlier than when onCreateView lifecycle method is called, hence the TextView is null until onCreateView. Now, onCreateView is called after onCreate, which in turn is called after onAttach, which is triggered by attaching the fragment to an Activity, usually through the use of FragmentManager. Calling CourseDetailsFragment.getInstance().updateUI(courseData) does not automatically trigger the fragment lifecycle, it only instantiates the Fragment instance, and as until that Fragment is attached to an Activity and onCreateView is called, your courseName is a null reference, which means that calling the updateUi method instantly after creation of the Fragment is doomed to result in a null pointer exception.

    The only option that this line of code would work without throwing exception is if this Fragment had been previously instantiated, attached to an activity and it had gone through the onCreateView method.

    For reasons described above, I don't recommend implementing fragments as singletons, especially if you want to make direct calls on a reference to such fragment from other components. Due to the lifecycles, Activities and Fragments are state machines in practice, and your updateUi method doesn't consider that state, which is the root of your problem.

    For a quick fix, you can override the onFragmentsResumed method in your Activity - if the fragment was previously attached, your references should be valid at that point.