java.lang.IllegalStateException:确保未从后台线程修改适配器的内容,但仅限于UI线程

时间:2015-02-15 18:05:03

标签: android listview android-listview adapter illegalstateexception

  java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296513, class xyz.ScrollDetectableListView) with Adapter(class android.widget.HeaderViewListAdapter)]

我有时会在滚动浏览动态列表视图然后点击项目时遇到异常。我研究了很多,但无法找到原因,为什么我有时会收到此错误以及如何解决?

     private ScrollDetectableListView mFListView;

    public FAdapter mFAdapter;    

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

                View headerView = getActivity().getLayoutInflater().inflate(R.layout.view_feed_header, null);
                try{
                mFListView = (ScrollDetectableListView) rootView.findViewById(R.id.feed_list_view);

                mFContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.feed_container);
                mFListView.addHeaderView(headerView);
                mFListView.setEmptyView(rootView.findViewById(R.id.empty_view));
                mFContainer.setColorSchemeResources(R.color.green, R.color.pink, R.color.fbcolor,
                        R.color.instagramcolor, R.color.googlecolor, R.color.flickrcolor);


                mFView = getActivity().getLayoutInflater().inflate(R.layout.view_footer, null);
                ImageView rotateImageView = (ImageView) mFooterView.findViewById(R.id.spinner);
                Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate);
                rotation.setFillAfter(false);
                rotateImageView.startAnimation(rotation);

                mFContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                    @Override
                    public void onRefresh()
                    {
                        initializeFListView();
                    }
                });

                initializeFListView();
                mProgressDialog.setVisibility(View.VISIBLE);

                mHActivity.setDataChangedListener(new DataChangedListener() {
                    @Override
                    public void onDataChanged() {

                        mFContainer.setRefreshing(true);
                        mProgressDialog.setVisibility(View.GONE);
                        initializeFListView();
                    }
                });
     }catch(Exception e){}
            return rootView;
        }

 public void initializeFListView()
    {
  FApi.getTrending(getActivity(), xyz, new APIResponseListener() {
                @Override
                public void onResponse(Object response) {

                    setFeedAdapter((List<Video>) response);
                }

                @Override
                public void onError(VolleyError error) {

                    if (error instanceof NoConnectionError) {
                        String errormsg = getResources().getString(R.string.no_internet_error_msg);
                        Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                    }

                }
            });
}
        private void setFAdapter(List<Video> response)
        {try {

            List<Video> videos = response;

            mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
            mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
            mFListView.setAdapter(mFAdapter);
            mProgressDialog.setVisibility(View.GONE);
            if (mFContainer.isRefreshing()) {
                mFContainer.setRefreshing(false);
            }
            if (mFAdapter.getCount() < mCount) {
                mFView.setVisibility(View.GONE);
                mFListView.removeFooterView(mFooterView);
            }
        }catch(Exception e){}
        }

    }

2 个答案:

答案 0 :(得分:0)

我的建议尝试在UI Thread ,,,

上设置列表适配器
private void setFAdapter(List<Video> response)
        {
        try {

            List<Video> videos = response;

            mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
            mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));


          runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    mFListView.setAdapter(mFAdapter);
                }
            });


            mProgressDialog.setVisibility(View.GONE);
            if (mFContainer.isRefreshing()) {
                mFContainer.setRefreshing(false);
            }
            if (mFAdapter.getCount() < mCount) {
                mFView.setVisibility(View.GONE);
                mFListView.removeFooterView(mFooterView);
            }
        }catch(Exception e){}
        }

    }

答案 1 :(得分:0)

  1. 保留一个单例类对象。这样你就可以同步两个线程了。注意不要阻止ui线程。

  2. 将接口数量减少到只有一种方法可以开始为列表准备数据,只有一种方法可以在列表中调用notifydatasetchanged / setAdapter。

    • 意味着应该只有一个方法,比如prepareData(),它将由后台线程执行。在你的单例对象上同步这个方法。

       MyListAdaper adapter = null;
      
       // Call this from a background thread
       public void prepareData() {
           synchronized (SingleTonProvider.getInstance()) {
                 List<AnyDataTypeYouWant> data = null;
                 // populate data here by your application logic.
                 adapter = new MyListAdaper(data);    
           }
       }   
      
    • 并且只有一种方法可以刷新列表。

       // Also Call this from a background thread only
       public void refreshList() {
      
           synchronized (SingleTonProvider.getInstance()) {
                 runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mFListView.setAdapter(adapter);
                     }
                 });
           }
       }
      
  3. 在任何地方都没有其他代码来准备数据并在列表上设置数据。

  4. 仅从后台线程调用我提到的方法。
  5. 我刚刚解决了你的问题。你必须自己处理你的具体案例。希望它有所帮助