【问题标题】:IllegalStateException on ArrayAdapterArrayAdapter 上的 IllegalStateException
【发布时间】:2014-12-20 01:05:07
【问题描述】:

我遇到了一个奇怪的问题。 我在列表视图上实例化了一个自定义 ArrayAdapter。 大多数时候,一切正常。

但 Crashlytics 日志显示以下异常:

Fatal Exception: 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(2131034300, class android.widget.ListView) with Adapter(class com.myapp.ui.adapters.CommentsAdapter)]
android.widget.ListView.layoutChildren (ListView.java:1566)
android.widget.AbsListView.onTouchMove (AbsListView.java:4718)
android.widget.AbsListView.onTouchEvent (AbsListView.java:4582)
android.view.View.dispatchTouchEvent (View.java:8112)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2414)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2138)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2420)
android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2153)
com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent (PhoneWindow.java:2252)
android.app.Activity.dispatchTouchEvent (Activity.java:2507)
android.support.v7.app.ActionBarActivityDelegateICS$WindowCallbackWrapper.dispatchTouchEvent (ActionBarActivityDelegateICS.java:260)

这很奇怪,因为当您更改适配器内容而没有立即调用 notifyDataSetChanged() 时会发生此异常。

ArrayAdapter 每次调用方法来添加或清除内部数据时都会调用它。

我知道当 notifyDatasetChanged 不是从主线程调用时可能会发生这种崩溃,在我的情况下,它总是从主线程调用。

我的适配器的主要部分:

public class CommentsAdapter extends ArrayAdapter<UserCommentShort> {

    CommentActionListener mCommentListener;
    LayoutInflater mInflater;
    final ImageLoader mImageLoader;

    private boolean mIsLoadMorePending = false;

    public CommentsAdapter(Context context, CommentActionListener commentListener) {
        super(context, 0);
        mInflater = LayoutInflater.from(context);
        mCommentListener = commentListener;
        mImageLoader = ImageLoader.getInstance();
    }

    @SuppressLint("NewApi")
    public void addAll(Collection<? extends UserCommentShort> collection, int nbComments) {
        // mNbComments = nbComments;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            addAll(collection);
        } else {
            for (final UserCommentShort userCommentShort : collection) {
                add(userCommentShort);
            }
        }
        mIsLoadMorePending = false;
    }

编辑

我调用填充的代码(@subscribe 注释由 otto event bus 使用)。

public class CommentsFragment extends AutoRefreshFragment implements OnScrollListener {
...

    @Subscribe
    public void onDataReadyEvent(DataReadyEvent evt) {

        switch (evt.getIdApi()) {
            case DisqusThreadOperation.API_GET_DISQUS_THREAD:
            case CommunityDisqusAction.API_COMMUNITY_DISQUS_LIKE:
            case CommunityDisqusAction.API_COMMUNITY_DISQUS_DISLIKE:
            case CommunityDisqusAction.API_COMMUNITY_DISQUS_ADD_COMMENT:

                if (!TextUtils.isEmpty(mCommentsThreadIdentifier)) {
                    if (DEBUG_MODE) {
                        Log.d(TAG, "onDataReadyEvent()=" + evt);
                    }
                    final BusinessDataWithDisqusThread disqusThread = CommentsUtils.getRequiredDataEvent(evt, mCommentsThreadIdentifier);

                    if (disqusThread != null) {
                        mDisqusThreadDetails = disqusThread.getDisqusThreadDetails();
                        if (mDisqusThreadDetails != null) {
                            updateCommentDrawer(mDisqusThreadDetails.getNbPosts(), disqusThread.getDisqusThreadDetails(), disqusThread.getUserComments());
                        }
                    }
                }
                break;
        }
    }

protected void updateCommentDrawer(Integer commentCount, DisqusThreadDetails thread, UserComments userComments) {

        if (commentCount == null) {
            mTvCommentsCount.setText("");
            mListView.setVisibility(View.INVISIBLE);
            mBtnCommentsViewAll.setVisibility(View.INVISIBLE);
            mEmptyView.setVisibility(View.VISIBLE);
        } else {
            if (commentCount == 0) {
                mListView.setVisibility(View.INVISIBLE);
            } else {
                mListView.setVisibility(View.VISIBLE);
            }

            displayData(thread, userComments);

            mBtnCommentsViewAll.setVisibility(View.VISIBLE);
            mCommentsTotalCount = commentCount.intValue();

            if (mCommentsTotalCount <= 0) {
                mTvCommentsCount.setVisibility(View.GONE);
                mEmptyView.setVisibility(View.VISIBLE);
            } else {
                mTvCommentsCount.setVisibility(View.VISIBLE);
                mEmptyView.setVisibility(View.GONE);
                final String qtyComments = CommentsUtils.getCommentCountString(mCommentsTotalCount, getActivity());
                mTvCommentsCount.setText(qtyComments);
            }
        }

        refreshConnectionStatus();
    }

    protected void displayData(DisqusThreadDetails thread, UserComments userComments) {
        mAdapter.clear();
        mAdapter.addAll(userComments.getCommentList(), thread.getNbPosts());
    }

【问题讨论】:

  • 发布您正在填充适配器数据的后台线程
  • 我添加了它。你够了吗?
  • 在 UI 线程中调用 displayData() 你会很安全
  • 正如我所写,回调是在主线程上完成的。 Otto 解释说: 由于有时您在哪个线程上接收回调可能不明确,因此 Otto 提供了一种强制机制来确保您始终在您想要的线程上被调用。默认情况下,与实例的所有交互都仅限于主线程。
  • 在 displayData() 方法中尝试 Log.d Thread.currentTread()

标签: android listview android-arrayadapter


【解决方案1】:

尝试修改您的displayData 方法:

protected void displayData(DisqusThreadDetails thread, UserComments userComments) {
    runOnUiThread(new Runnable() { public void run() {
        mAdapter.clear();
        mAdapter.addAll(userComments.getCommentList(), thread.getNbPosts());
    }});
}

【讨论】:

  • 我的片段是否可能不使用 UiThread ?我添加了日志以查看使用的线程 => Thread.currentTread()=Thread[main,5,main]。最重要的是,大多数时候,这段代码都有效。为什么回调有时会在不同的线程上运行...
  • 优酷,你在哪里看到的?我从runOnUiThread中检查了Thread.currentThread(),在里面,结果是一样的。可以肯定的是,我还检查了 Looper.myLooper() == Looper.getMainLooper(),它在 runOnUiThread 内外都是一样的。
猜你喜欢
  • 2012-12-15
  • 2013-07-12
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
相关资源
最近更新 更多