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好的,我试过了