I have seen many other questions on this topic/error, but none seem to be in my situation. The below LogCat
related to my Nav Drawer list adapter
.
(brief code below the 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)
Inside my onCreate()
in my MainActivity
I simply do this:
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);
At no point do I add anything to this list. The only time does it change is if a user "upgrades" the app, and then I remove an item.
This is the code that does that:
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();
}
}
};
I think I have been reading that you are not suppose to add items or do notifyDataSetChanged() in a background thread. Not sure if that is a background thread above? Even so, I do not believe this is being triggered when the error happens. (But I won't totally discount it).
If the user does upgrade, then when the app starts -- since the upgrade menu item is the last item and i no longer need to show it, I simply do this:
in DrawerAdapter
:
@Override
public int getCount() {
if (MyApp.didUpgrade) {
return 12;
} else {
return 13;
}
}
I can't think of any other code.
All feedback is appreciated!
EDIT:
Updated code based on recommendation. The one instance of notifyDataSetChanged()
is now being moved to a UI thread. However, this code is only called after the user directly clicks the in-app purchase and upgrades. I don't believe it is called again. The user with this error is reporting everytime they enter the app and click on a menu item.
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();
}
UPDATE #2
The user reported this bug after a recent update, I had did. The app had not been updated for 9 months. The only changes I really did was chagne the targetSdkVersion from 23 -> 25. Also upgrade the depenencies. I do not recall the old versions, the news one are
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'
NO code in my Nav drawer or adapter was otherwise changed int his update.
I suggest you check for any other places where you write to
MyApp.didUpgrade
.Perhaps in a callback from
mHelper.queryInventoryAsync(..)
?I suspect the following:
1. You call
mHelper.queryInventoryAsync
to check the users inventory. The call is async, therefore the thread will wait for the answer.2. The activity and its drawer content is inflated, the list is populated with 13 items.
3.
queryInventoryAsync
now returns and triggers your callback, you setMyApp.didUpgrade = true
but forget to callnotifyDataSetChanged()
.4. For any reason the drawer content is redrawn - an exception is thrown because
count()
now returns 12 and android believes the list is corrupt.Do you recieve error reports only from users who have already bought the upgrade?
Note also that the view in the drawer is inflated as soon as the activty is created, even if the drawer is not opened.