IllegalStateException 表示调用 'notifyDataSetChanged() when its content changes'
IllegalStateException says to call 'notifyDataSetChanged() when its content changes'
关于这个 topic/error 我已经看到很多其他问题,但是 none 似乎符合我的情况。以下 LogCat
与我的导航抽屉列表 adapter
.
相关
(LogCat
下方的简短代码)
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(2131558613, class android.widget.ListView) with Adapter(class com.---.---.DrawerAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1572)
at android.widget.AbsListView.onTouchUp(AbsListView.java:3959)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:3758)
at android.view.View.dispatchTouchEvent(View.java:9349)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2553)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2240)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at com.android.internal.policy.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2453)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1755)
at android.app.Activity.dispatchTouchEvent(Activity.java:2776)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:67)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:67)
at com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2402)
at android.view.View.dispatchPointerEvent(View.java:9590)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4436)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4292)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3816)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3875)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3841)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3971)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4028)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3821)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3875)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3841)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3821)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6150)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6118)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6072)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6253)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:216)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:144)
at android.app.ActivityThread.main(ActivityThread.java:5845)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
在我的 onCreate()
在我的 MainActivity
我只是这样做:
mDrawerList = (ListView) findViewById(R.id.drawer_list);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
String[] calcTypeList = new String[]{"item1", "item2", "etc".}; // 13 items total
// override with new list if user upgraded? (1 less item)
if (myApp.didUpgrade) {
calcTypeList = new String[]{"item1", "item2", "etc".}; // 12 items total
}
dAdapter = new DrawerAdapter(MainFragmentActivity.this, lcTypeList);
mDrawerList.setAdapter(dAdapter);
我绝不会向此列表中添加任何内容。唯一一次改变是如果用户 "upgrades" 应用程序,然后我删除了一个项目。
这是执行此操作的代码:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase) {
if (result.isFailure()) {
Log.i("Purchase Result", "There was a problem with the purchase: " + result.getMessage());
return;
} else if (purchase.getSku().equals(SKU_UPGRADE)) {
Toast.makeText(MainFragmentActivity.this, "Upgrade Successful!", Toast.LENGTH_SHORT).show();
MyApp.didUpgrade = true;
dAdapter.notifyDataSetChanged();
}
}
};
我 认为 我一直在读到您不应该在后台线程中添加项目或执行 notifyDataSetChanged()。不确定上面是否是后台线程?即便如此,我不相信这是在错误发生时被触发的。 (但我不会完全打折)。
如果用户升级,那么当应用程序启动时——因为升级菜单项是最后一项,我不再需要显示它,所以我简单地这样做:
在DrawerAdapter
中:
@Override
public int getCount() {
if (MyApp.didUpgrade) {
return 12;
} else {
return 13;
}
}
我想不出任何其他代码。
感谢所有反馈!
编辑:
根据建议更新了代码。 notifyDataSetChanged()
的一个实例现在被移动到 UI 线程。但是这段代码只有在用户直接点击内购升级后才会被调用。我不相信它会被再次调用。出现此错误的用户每次进入应用程序并单击菜单项时都会报告。
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase) {
if (result.isFailure()) {
Log.i("Purchase Result", "There was a problem with the purchase: " + result.getMessage());
return;
} else if (purchase.getSku().equals(SKU_UPGRADE)) {
Toast.makeText(MainFragmentActivity.this, "Upgrade Successful!", Toast.LENGTH_SHORT).show();
MyApp.didUpgrade = true;
setDrawerAdapter();
}
}
};
private void setDrawerAdapter() {
new Thread() {
public void run() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
dAdapter.notifyDataSetChanged();
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
更新 #2
用户在最近更新后报告了这个错误,我也报告过。该应用程序已 9 个月未更新。我真正做的唯一改变是将 targetSdkVersion 从 23 -> 25 更改。同时升级依赖项。我不记得旧版本,新闻是
compile "com.android.support:appcompat-v7:25.0.1"
compile 'com.android.support:cardview-v7:25.0.1'
compile 'com.android.support:design:25.0.1'
我的 Nav 抽屉或适配器中的代码在他的更新中没有更改。
我搜索了一下这个问题:
ListView
缓存元素计数,每当它必须对其子项进行布局时,它会再次检查此计数与绑定适配器中的当前项目数。如果计数已更改且未通知 ListView,则会引发错误:
else if (mItemCount != mAdapter.getCount()) {
throw new 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(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}
我不确定是否在另一个线程上调用了 onIabPurchaseFinished()
。但如果你真的不改变其他地方的任何东西,我认为这就是问题所在。
比方说,它在另一个线程上。因此,当您调用 MyApp.didUpgrade = true;
时,适配器内容会被另一个线程更改,因为您在适配器内部调用了 if (MyApp.didUpgrade) {
。然后 listview 调用 layoutChildren()
并具有旧的 getcount() 数。我认为这就是您收到此错误的原因。
尝试调用 runOnUiThread()
中的两条线路
MyApp.didUpgrade = true;
dAdapter.notifyDataSetChanged();
我建议您检查一下您写信给 MyApp.didUpgrade
的任何其他地方。
也许在 mHelper.queryInventoryAsync(..)
的回调中?
我怀疑如下:
1. 您致电 mHelper.queryInventoryAsync
查询用户库存。调用是异步的,因此线程将等待答案。
2. activity 及其抽屉内容膨胀,列表中填充了 13 个项目。
3. queryInventoryAsync
现在 returns 并触发你的回调,你设置 MyApp.didUpgrade = true
但忘记调用 notifyDataSetChanged()
.
4. 由于任何原因重绘抽屉内容 - 抛出异常,因为 count()
现在 returns 12 并且 android 认为列表已损坏。
您是否只收到已购买升级的用户的错误报告?
另请注意,一旦创建活动,抽屉中的视图就会膨胀,即使抽屉未打开也是如此。
关于这个 topic/error 我已经看到很多其他问题,但是 none 似乎符合我的情况。以下 LogCat
与我的导航抽屉列表 adapter
.
(LogCat
下方的简短代码)
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(2131558613, class android.widget.ListView) with Adapter(class com.---.---.DrawerAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1572)
at android.widget.AbsListView.onTouchUp(AbsListView.java:3959)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:3758)
at android.view.View.dispatchTouchEvent(View.java:9349)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2553)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2240)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2559)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2260)
at com.android.internal.policy.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2453)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1755)
at android.app.Activity.dispatchTouchEvent(Activity.java:2776)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:67)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:67)
at com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2402)
at android.view.View.dispatchPointerEvent(View.java:9590)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4436)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4292)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3816)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3875)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3841)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3971)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4028)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3821)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3875)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3841)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3821)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6150)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6118)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6072)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6253)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:216)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:144)
at android.app.ActivityThread.main(ActivityThread.java:5845)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
在我的 onCreate()
在我的 MainActivity
我只是这样做:
mDrawerList = (ListView) findViewById(R.id.drawer_list);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
String[] calcTypeList = new String[]{"item1", "item2", "etc".}; // 13 items total
// override with new list if user upgraded? (1 less item)
if (myApp.didUpgrade) {
calcTypeList = new String[]{"item1", "item2", "etc".}; // 12 items total
}
dAdapter = new DrawerAdapter(MainFragmentActivity.this, lcTypeList);
mDrawerList.setAdapter(dAdapter);
我绝不会向此列表中添加任何内容。唯一一次改变是如果用户 "upgrades" 应用程序,然后我删除了一个项目。
这是执行此操作的代码:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase) {
if (result.isFailure()) {
Log.i("Purchase Result", "There was a problem with the purchase: " + result.getMessage());
return;
} else if (purchase.getSku().equals(SKU_UPGRADE)) {
Toast.makeText(MainFragmentActivity.this, "Upgrade Successful!", Toast.LENGTH_SHORT).show();
MyApp.didUpgrade = true;
dAdapter.notifyDataSetChanged();
}
}
};
我 认为 我一直在读到您不应该在后台线程中添加项目或执行 notifyDataSetChanged()。不确定上面是否是后台线程?即便如此,我不相信这是在错误发生时被触发的。 (但我不会完全打折)。
如果用户升级,那么当应用程序启动时——因为升级菜单项是最后一项,我不再需要显示它,所以我简单地这样做:
在DrawerAdapter
中:
@Override
public int getCount() {
if (MyApp.didUpgrade) {
return 12;
} else {
return 13;
}
}
我想不出任何其他代码。
感谢所有反馈!
编辑:
根据建议更新了代码。 notifyDataSetChanged()
的一个实例现在被移动到 UI 线程。但是这段代码只有在用户直接点击内购升级后才会被调用。我不相信它会被再次调用。出现此错误的用户每次进入应用程序并单击菜单项时都会报告。
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase) {
if (result.isFailure()) {
Log.i("Purchase Result", "There was a problem with the purchase: " + result.getMessage());
return;
} else if (purchase.getSku().equals(SKU_UPGRADE)) {
Toast.makeText(MainFragmentActivity.this, "Upgrade Successful!", Toast.LENGTH_SHORT).show();
MyApp.didUpgrade = true;
setDrawerAdapter();
}
}
};
private void setDrawerAdapter() {
new Thread() {
public void run() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
dAdapter.notifyDataSetChanged();
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
更新 #2
用户在最近更新后报告了这个错误,我也报告过。该应用程序已 9 个月未更新。我真正做的唯一改变是将 targetSdkVersion 从 23 -> 25 更改。同时升级依赖项。我不记得旧版本,新闻是
compile "com.android.support:appcompat-v7:25.0.1"
compile 'com.android.support:cardview-v7:25.0.1'
compile 'com.android.support:design:25.0.1'
我的 Nav 抽屉或适配器中的代码在他的更新中没有更改。
我搜索了一下这个问题:
ListView
缓存元素计数,每当它必须对其子项进行布局时,它会再次检查此计数与绑定适配器中的当前项目数。如果计数已更改且未通知 ListView,则会引发错误:
else if (mItemCount != mAdapter.getCount()) {
throw new 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(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}
我不确定是否在另一个线程上调用了 onIabPurchaseFinished()
。但如果你真的不改变其他地方的任何东西,我认为这就是问题所在。
比方说,它在另一个线程上。因此,当您调用 MyApp.didUpgrade = true;
时,适配器内容会被另一个线程更改,因为您在适配器内部调用了 if (MyApp.didUpgrade) {
。然后 listview 调用 layoutChildren()
并具有旧的 getcount() 数。我认为这就是您收到此错误的原因。
尝试调用 runOnUiThread()
MyApp.didUpgrade = true;
dAdapter.notifyDataSetChanged();
我建议您检查一下您写信给 MyApp.didUpgrade
的任何其他地方。
也许在 mHelper.queryInventoryAsync(..)
的回调中?
我怀疑如下:
1. 您致电 mHelper.queryInventoryAsync
查询用户库存。调用是异步的,因此线程将等待答案。
2. activity 及其抽屉内容膨胀,列表中填充了 13 个项目。
3. queryInventoryAsync
现在 returns 并触发你的回调,你设置 MyApp.didUpgrade = true
但忘记调用 notifyDataSetChanged()
.
4. 由于任何原因重绘抽屉内容 - 抛出异常,因为 count()
现在 returns 12 并且 android 认为列表已损坏。
您是否只收到已购买升级的用户的错误报告?
另请注意,一旦创建活动,抽屉中的视图就会膨胀,即使抽屉未打开也是如此。