getContext() crashes my App when calling update RecyclerView function?

66 Views Asked by At

my app crashes all the time, when I want to access a specific fragment. The error is:

FATAL EXCEPTION: main Process: com.akstudios.kindergarten, PID: 15680 java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager)' on a null object reference at com.akstudios.kindergarten.GroupbookKrippeFragment.setRecyclerView(GroupbookKrippeFragment.java:56)

I saw a lot of people had similar problems in the past, but no ideas or suggestions helped me to fix mine.

I tried to get the context with getContext(), onAttach(), GroupbookKrippeFragment.this.getContext() and some other ways.

UPDATE START (12.11.22)

I as able to narrow down the problem a bit more. The app is stable and working until I made the following change:

1.) For updating my tableList with recyclerview entries coming from my database, I encapsulate the following code to a new method within the GroupbookKrippeFragment.java:

public void setRecyclerView(List<ChildrenTable> childrenTableList) {
        recycler_view.setLayoutManager(new LinearLayoutManager(getContext()));
        childAdapter = new ChildAdapter(getContext(), childrenTableList);
//        childAdapter = new ChildAdapter(getContext());
        recycler_view.setAdapter(childAdapter);
    }

2.) I called this method in my DatabaseHelper.java class on the onPostExecute() AsyncTask of AllChildren:

public class DatabaseHelper {
Context context;
GroupbookKrippeFragment groupbookKrippeFragment = new GroupbookKrippeFragment();

public DatabaseHelper(Context context) {
    this.context = context;
}

public static DatabaseHelper getInstance(Context context) {
    return new DatabaseHelper(context);
}

// Insert Data
// TODO: Add all columns as Input parameter for Database
public void addNewChild(
        String child_name, String child_surname, String child_birthday,
        String child_birthday_location, String child_confession, String child_nationality) {

    class NewChild extends AsyncTask<Void, Void, ChildrenTable> {

        @Override
        protected ChildrenTable doInBackground(Void... voids) {
            ChildrenTable childrenTable = new ChildrenTable();

            // TODO: Link all columns for Database
            childrenTable.setChild_name(child_name);
            childrenTable.setChild_surname(child_surname);
            childrenTable.setChild_birthday(child_birthday);
            childrenTable.setChild_birthday_location(child_birthday_location);
            childrenTable.setChild_confession(child_confession);
            childrenTable.setChild_nationality(child_nationality);

            DatabaseClient.getInstance(context)
                    .getChildrenDatabase()
                    .childrenDAO()
                    .insertData(childrenTable);
            return childrenTable;
        }

        @Override
        protected void onPostExecute(ChildrenTable childrenTable) {
            super.onPostExecute(childrenTable);
            if (childrenTable != null) {
                Toast.makeText(
                        context,
                        childrenTable.getChild_name() + "\n" +
                        childrenTable.getChild_surname(), Toast.LENGTH_SHORT).show();
            }
        }
    }

    NewChild newChild = new NewChild();
    newChild.execute();
}

// Show all data from ChildrenTable
public void getAllChildrenData() {
    class AllChildren extends AsyncTask<Void, Void, List<ChildrenTable>> {

        @Override
        protected List<ChildrenTable> doInBackground(Void... voids) {
            List<ChildrenTable> list = DatabaseClient.getInstance(context)
                    .getChildrenDatabase()
                    .childrenDAO()
                    .selectAll();
            return list;
        }

        @Override
        protected void onPostExecute(List<ChildrenTable> childrenTable) {
            super.onPostExecute(childrenTable);
            if (childrenTable != null && childrenTable.size() > 0) {
                groupbookKrippeFragment.setRecyclerView(childrenTable);
            }

        }
    }

    AllChildren allChildren = new AllChildren();
    allChildren.execute();
}

}

Whenever I comment out this method call and move the lines from the method into onViewCreated of GroupbookKrippeFragment, the app is stable again, but the recyclerView data is not showing and updating with my database.

UPDATE END (12.11.22)

Thanks a lot for your help in advance - here are my files:

GroupbookKrippeFragment.java

public class GroupbookKrippeFragment extends Fragment {

Button addChildBtn;
RecyclerView recycler_view;
ChildAdapter childAdapter;
DatabaseHelper helper;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_groupbook_krippe, container, false);

    addChildBtn = view.findViewById(R.id.btn_groupBook_krippe_1);
    addChildBtn.setOnClickListener(view1 -> {
        Intent intent = new Intent(GroupbookKrippeFragment.this.getActivity(), GroupbookAddChildFragment.class);
        startActivity(intent);
    });

    recycler_view = view.findViewById(R.id.recycler_view);

    helper = DatabaseHelper.getInstance(getContext());
    helper.getAllChildrenData();

    return view;
}

public void setRecyclerView(List<ChildrenTable> childrenTableList) {
    recycler_view.setLayoutManager(new LinearLayoutManager(getContext()));
    childAdapter = new ChildAdapter(getContext(), childrenTableList);
    recycler_view.setAdapter(childAdapter);
  }
}

GroupbookFragment.java

public class GroupbookFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_groupbook, container, false);

    // Tab layout initialization
    TabLayout tabLayout = view.findViewById(R.id.tabLayout);
    ViewPager2 viewPager = view.findViewById(R.id.viewPager_groupBook);
    GroupbookAdapter adapterGroupBook = new GroupbookAdapter(getChildFragmentManager(), getLifecycle());
    viewPager.setAdapter(adapterGroupBook);

    // set 3 Tab titles
    tabLayout.addTab(tabLayout.newTab().setText("Krippe"));
    tabLayout.addTab(tabLayout.newTab().setText("Hafen"));
    tabLayout.addTab(tabLayout.newTab().setText("Kindergarten"));

    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            viewPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

    viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageSelected(int position) {
            tabLayout.selectTab(tabLayout.getTabAt(position));
        }
    });
    return view;
  }
}
1

There are 1 best solutions below

0
laalto On

Your GroupbookKrippeFragment owns a DatabaseHelper. Your DatabaseHelper owns another instance of GroupbookKrippeFragment that is not involved in a fragment transaction and hence its lifecycle methods such as onCreateView() are not invoked, and recycler_view remains null in that instance.

Rather than DatabaseHelper instantiating a new GroupbookKrippeFragment, you could e.g. pass in a reference to the owning fragment as a constructor argument, store it in a field, and call setRecyclerView() on it later.

It's not a perfect design but helps you one step forward in your development journey.