IllegalStateException表示当其内容发生变化时调用' notifyDataSetChanged()'

时间:2016-12-28 17:51:25

标签: android listview android-arrayadapter illegalstateexception

我已经看到很多关于这个主题/错误的其他问题,但似乎没有一个在我的情况下。以下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()的{​​{1}}内,我只是这样做:

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);

我一直在读你不想添加项目或在后台线程中执行notifyDataSetChanged()。不确定这是否是上面的后台线程?即使这样,我也不相信错误发生时会触发这个。 (但我不会完全打折)。

如果用户确实升级了,那么当应用程序启动时 - 由于升级菜单项是最后一项,我不再需要显示它,我只是这样做:

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(); } } }; 中的

DrawerAdapter

我无法想到任何其他代码。

感谢所有反馈!

修改

根据建议更新了代码。 @Override public int getCount() { if (MyApp.didUpgrade) { return 12; } else { return 13; } } 的一个实例现在正被移动到UI线程。但是,只有在用户直接单击应用内购买和升级后才会调用此代码。我不相信它再次被召唤。出现此错误的用户每次进入应用程序时都会报告并单击菜单项。

notifyDataSetChanged()

更新#2

用户在最近的更新后报告了这个错误,我做过。该应用程序尚未更新9个月。我真正做的唯一改变就是从23开始查看targetSdkVersion - > 25.还要升级附加费。我不记得旧版本,新闻一个

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 个答案:

答案 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) {。然后列表视图调用layoutChildren()并具有旧数量的getcount()。我想这就是你得到这个错误的原因。

尝试在runOnUiThread()

中调用这两行
MyApp.didUpgrade = true;
dAdapter.notifyDataSetChanged();

答案 1 :(得分:1)

我建议你查看你写给MyApp.didUpgrade的其他任何地方 也许在mHelper.queryInventoryAsync(..)的回调中?

我怀疑以下内容:
1.您致电mHelper.queryInventoryAsync查看用户广告资源。该调用是异步的,因此线程将等待答案 2.活动及其抽屉内容被夸大,列表中包含13个项目 3. queryInventoryAsync现在返回并触发您的回调,您设置MyApp.didUpgrade = true但忘记拨打notifyDataSetChanged()
4.由于任何原因重绘了抽屉内容 - 抛出异常,因为count()现在返回12并且android认为该列表已损坏。

您是否仅向已购买升级的用户收到错误报告? 另请注意,即使抽屉未打开,创建激活时抽屉中的视图也会膨胀。