【发布时间】: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