Android IllegalStateException说要调用';notifyDataSetChanged()当其内容更改时';
我见过很多关于这个话题/错误的其他问题,但没有一个是我的情况。下面的Android IllegalStateException说要调用';notifyDataSetChanged()当其内容更改时';,android,listview,android-arrayadapter,illegalstateexception,Android,Listview,Android Arrayadapter,Illegalstateexception,我见过很多关于这个话题/错误的其他问题,但没有一个是我的情况。下面的LogCat与我的导航抽屉列表适配器相关 (日志猫下方的简短代码) 在我的onCreate()中在我的main活动中我只需执行以下操作: mDrawerList = (ListView) findViewById(R.id.drawer_list); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); S
LogCat
与我的导航抽屉列表适配器相关
(日志猫下方的简短代码
)
在我的onCreate()中
在我的main活动中
我只需执行以下操作:
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);
在任何时候我都不能在这个列表中添加任何内容。唯一的改变是,如果用户“升级”应用程序,然后我删除了一个项目
这是执行以下操作的代码:
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()。不确定这是否是上面的背景线程?即使如此,我也不认为这是在错误发生时触发的。(但我不会完全打折)
如果用户确实升级了,那么当应用程序启动时——因为升级菜单项是最后一项,我不再需要显示它,所以我只需执行以下操作:
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);
在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
用户在最近的一次更新后报告了这个bug,我已经报告了。该应用程序已有9个月未更新。我真正做的唯一改变是从23->25更改targetSdkVersion。同时升级从属关系。我不记得旧版本了,新闻版本是
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'
在这次更新中,我的导航抽屉或适配器中没有任何代码被更改。我搜索了一些有关此问题的信息:
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()中的两行
我搜索了一下这个问题:
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
可能在mHelper.queryInventoryAsync(..)
的回调中
我怀疑如下:
1.调用mHelper.queryInventoryAsync
检查用户资源清册。该调用是异步的,因此线程将等待回答。
2.活动及其抽屉内容已膨胀,列表中填充了13项。
3.queryInventoryAsync
现在返回并触发回调,您可以设置MyApp.didUpgrade=true
但忘记调用notifyDataSetChanged()
4.由于任何原因,抽屉内容被重新绘制-引发异常,因为count()
现在返回12,并且android认为列表已损坏
您是否仅从已购买升级的用户处收到错误报告?
另请注意,创建活动后,即使抽屉未打开,抽屉中的视图也会立即膨胀。我建议您检查是否有其他写入MyApp.didUpgrade的地方。
可能在mHelper.queryInventoryAsync(..)
的回调中
我怀疑如下:
1.调用mHelper.queryInventoryAsync
检查用户资源清册。该调用是异步的,因此线程将等待回答。
2.活动及其抽屉内容已膨胀,列表中填充了13项。
3.queryInventoryAsync
现在返回并触发回调,您可以设置MyApp.didUpgrade=true
但忘记调用notifyDataSetChanged()
4.由于任何原因,抽屉内容被重新绘制-引发异常,因为count()
现在返回12,并且android认为列表已损坏
您是否仅从已购买升级的用户处收到错误报告?
另请注意,创建活动后,抽屉中的视图会立即膨胀,即使抽屉未打开。您是否尝试了runOnUiThread()
?@beeb否我没有。我应该将其放在哪里?列表本身构建在UI线程上。尝试将其放在以下位置:dAdapter.notifyDataSetChanged()
这只是个主意…@beep好的,我试过了