ListFragment有时会跳过Loader的onLoadFinished()方法中的代码

ListFragment sometimes skips code in onLoadFinished() method of Loader

我有一个 ListFragment,它使用 Loader 从网上获取数据。我在 ViewPager 的每一页中都使用了这个 ListFragment 的新实例。它工作得很好,但是当我使用 TabLayout 或快速移动页面时,Fragment 一直在加载并且不显示 ListView.

中的数据

当我使用日志消息检查时,我发现 ListFragment 跳过了 onLoadFinished() 方法中的一些代码行。它不会使 ProgressBar 不可见。它确实将项目添加到 Adapter,但未显示在 ListView 中。 ViewPager.

的第一页也出现了这个问题

ViewPager 中使用 ListFragment 时是否有任何特定规则要遵循?

这里是ListFragmentclass。如果您查看 onLoadFinished() 方法,您可以看到导致问题的行:

public class ListViewFragment extends ListFragment
    implements LoaderManager.LoaderCallbacks<List<GameNews>> {

    public static ListViewFragment newInstance(String url) {
        Log.d("ListViewFragment", "newInstance created");
        ListViewFragment f = new ListViewFragment();

        // Supply url input as an argument.
        Bundle args = new Bundle();
        args.putString("url", url);
        f.setArguments(args);
        return f;
    }

    List<GameNews> TotalNews;
    ListView gameListView;
    LinearLayout emptyView;
    Button retryButton;
    ListAdapter adapter ;
    private View progressBar;
    final private int game_loader = 0;
    ArrayList<String> urls = new ArrayList<>();
    String mUrlString;
    int index;

    //LIFE CYCLE OF FRAGMENT
    //------------------------------------------------------------------
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUrlString = getArguments().getString("url");
        urls.add(mUrlString);
        TotalNews = new ArrayList<GameNews>();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_list_view, container, false);

        ArrayList<GameNews> gameList = new ArrayList<>();
        adapter = new ListAdapter(getActivity(), gameList);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        emptyView = (LinearLayout)
            view.findViewById(R.id.no_internet_view);
        progressBar = view.findViewById(R.id.progress_bar);
        retryButton = (Button) view.findViewById(R.id.retry_button);
        gameListView = getListView();
        emptyView.setVisibility(View.INVISIBLE);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setListAdapter(adapter);

        //If connected to net start the loader
        if (isConnected()) {
            getActivity().getSupportLoaderManager().restartLoader(game_loader,
                                                                  null,
                                                                  ListViewFragment.this);
        }
    }

    //OVERRIDED METHODS OF LOADERMANAGER
    //---------------------------------------------------------------------
    @Override
    public android.support.v4.content.Loader<List<GameNews>> onCreateLoader(int i, Bundle bundle) {
        AdManager manager =  new AdManager(getActivity());
        return new FragmentLoader(getActivity(), urls, 1000);

    }

    //HERE IS THE PROBLEM PLEASE FOCUS INSIDE THIS METHOD
    //-------------------------------------------------------
    @Override
    public void onLoadFinished(Loader<List<GameNews>> loader, List<GameNews> games) {

        progressBar.setVisibility(View.INVISIBLE); //This line of code is not executed
        adapter.clear();
        TotalNews.addAll(games);
        adapter.addAll(games);//And the listView is not populated
    }
    //-------------------------------------------------------

    @Override
    public void onLoaderReset(Loader<List<GameNews>> loader) {
        adapter.clear();
    }

    //REUSABLE METHODS
    //------------------------------------------------------------------
    //Method checks if there is internet
    public boolean isConnected() {
        ConnectivityManager manager = (ConnectivityManager)
            getActivity().getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            return true;
        }
        else {
            return false;
        }
    }
}

您的 Fragment class 正在使用 ActivityLoaderManager:

getActivity().getSupportLoaderManager().restartLoader(...);

并且每个实例在其 restartLoader() 调用中使用相同的 ID:

final private int game_loader = 0;

这意味着每个 Fragment 实例都在一次又一次地使用和重新启动相同的 Loader,导致您观察到的奇怪行为。

解决方案非常简单:使用 Fragment 的本地 LoaderManager,而不是 Activity 的本地

getLoaderManager().restartLoader(...);

有了这个,您就不必担心在每个实例中更改 ID,因为 Loader 对它们的 Fragment 来说是唯一的,并且 Loader 将是正确的在 Fragment 的生命周期内处理,使用 ActivityLoaderManager.

时可能不会出现这种情况