0

So I have a problem of understanding (i think) of how refreshing UI works. I have an activity with a bottom navigation bar with 3 items that load a fragment. Each fragment load data from internet so I download these data into a thread and when it's finished I refresh my recyclerview on the UI Thread.

But the problem is when I select an item on the navigation bar and when i immediately select an other item my app crashes because the download of the data is finished and the UI thread is trying to refresh the recyclerview but i'm no longer on the fragment

So how can I manage to avoid this crash ?

Here is my code :

private void refreshRecyclerView() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                predictionList = ComboBetHTMLParser.getPredictionList();
                updateUI();
            }
        }).start();
    }

    private void updateUI() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter = new PredictionAdapter(predictionList);
                recyclerView.setAdapter(adapter);
                adapter.notifyDataSetChanged();
            }
        });
    }

EDIT :

Here is the error log :

                  java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.FragmentActivity.runOnUiThread(java.lang.Runnable)' on a null object reference
                      at com.projects.morgan.kingwin.Fragments.SafeFragment.updateUI(SafeFragment.java:61)
                      at com.projects.morgan.kingwin.Fragments.SafeFragment.access$100(SafeFragment.java:20)
                      at com.projects.morgan.kingwin.Fragments.SafeFragment$1.run(SafeFragment.java:55)
                      at java.lang.Thread.run(Thread.java:764)

Main Activity bottom navigation bar code :

bottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.OnTabSelectedListener() {
            @Override
            public void onTabSelected(int position) {
                displayOnSelectedScreen(position);
            }

            @Override
            public void onTabUnselected(int position) {
                Log.d("TEST", "onTabUnselected:" + position);
            }

            @Override
            public void onTabReselected(int position) {
                Log.d("TEST", "onTabReselected:");
            }
        });



private void displayOnSelectedScreen(int position) {
        // Begin the transaction
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment fragment = null;
        switch (position) {
            case 0:
                if (accueilFragment == null) {
                    accueilFragment = new AccueilFragment();
                }
                fragment = accueilFragment;
                break;
            case 1:
                if (predictionsFragment == null) {
                    predictionsFragment = new PredictionsFragment();
                }
                fragment = predictionsFragment;
                break;
            case 2:
                if (safeFragment == null) {
                    safeFragment = new SafeFragment();
                }
                fragment = safeFragment;
                break;
        }
        ft.replace(R.id.content_frame,fragment).commit();
    }

Fragment code :

public class SafeFragment extends Fragment {

    private List<Prediction> predictionList = new ArrayList<>();
    private RecyclerView recyclerView;
    private PredictionAdapter adapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.recycler_view_pronos, container, false);
        initRecyclerView(view);
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (predictionList.size() == 0) {
            refreshRecyclerView();
        }
    }

    private void initRecyclerView(View view) {
        recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        adapter = new PredictionAdapter(predictionList);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView.setAdapter(adapter);
    }

    private void refreshRecyclerView() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                predictionList = InternetData.getPredictionList();
                updateUI();
            }
        }).start();
    }

    private void updateUI() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter = new PredictionAdapter(predictionList);
                recyclerView.setAdapter(adapter);
                adapter.notifyDataSetChanged();
            }
        });
    }
}

4 Answers4

0

Please Provide whole fragments code and their host activity. It seems some concurrency issues exist about your case.

Mahdi Rajabi
  • 582
  • 1
  • 4
  • 11
0

Add a null pointer check before calling your runOnUiThread method like below

if(getActivity() != null){
    getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
            adapter = new PredictionAdapter(predictionList);
            recyclerView.setAdapter(adapter);
            adapter.notifyDataSetChanged();
        }
    });
}

Because your fragment no longer exists, its getActivity() method returns null. This should avoid the crash.

rbd_sqrl
  • 420
  • 4
  • 14
0

I think the problem is while you are tring to get the reference of getActivity() to run on ui thread.

private void updateUI() {
    getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
            adapter = new PredictionAdapter(predictionList);
            recyclerView.setAdapter(adapter);
            adapter.notifyDataSetChanged();
        }
    });
}

If you are not in the fragment and trying to call getActivity() it will return null object. So add null check before calling getActivity() will solve the problem. something like

    if(getActivity() !=null){
    //call the ui thread
      getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter = new PredictionAdapter(predictionList);
                recyclerView.setAdapter(adapter);
                adapter.notifyDataSetChanged();
            }
        });
    }
Durga M
  • 534
  • 5
  • 17
0

You have to check first that whether your current activity or fragment is present or not.Because you are trying to update the UI but may be at that time fragment or activity not there. You can do this by using below code :

if (getActivity() != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    adapter = new PredictionAdapter(predictionList);
                    recyclerView.setAdapter(adapter);
                    adapter.notifyDataSetChanged();
                }
            });
        }
abhayd
  • 3
  • 1
  • 5