在 Thread 上刷新 UI 并更改 Fragment 时出错

Error when refreshing UI on Thread and changing Fragment

所以我在理解(我认为)刷新 UI 的工作原理时遇到了问题。 我有一个带有底部导航栏的 activity,其中包含 3 个加载片段的项目。 每个片段都从互联网加载数据,所以我将这些数据下载到一个线程中,完成后我在 UI 线程上刷新我的 recyclerview。

但问题是,当我 select 导航栏上的一个项目时,当我立即 select 另一个项目时,我的应用程序崩溃了,因为数据下载已完成并且 UI 线程正在尝试刷新 recyclerview,但我不再在片段上了

那么我怎样才能避免这次崩溃呢?

这是我的代码:

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();
            }
        });
    }

编辑:

这是错误日志:

                  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.access0(SafeFragment.java:20)
                      at com.projects.morgan.kingwin.Fragments.SafeFragment.run(SafeFragment.java:55)
                      at java.lang.Thread.run(Thread.java:764)

主Activity底部导航条码:

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();
    }

片段代码:

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();
            }
        });
    }
}

请提供完整的片段代码及其宿主activity。您的案例似乎存在一些并发问题。

在调用您的 runOnUiThread 方法之前添加空指针检查,如下所示

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

由于您的片段不再存在,其 getActivity() 方法 returns 为空。这应该可以避免崩溃。

我认为问题出在您试图在 ui 线程上获取对 运行 的 getActivity() 的引用。

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

如果您不在片段中并试图调用 getActivity() 它将 return null 对象。 所以在调用getActivity()之前添加null检查将解决问题。 像

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

您必须首先检查您当前的 activity 或片段是否存在,或者 not.Because 您正在尝试更新 UI 但可能当时片段或 activity 不存在。 您可以使用以下代码执行此操作:

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