Reputation: 988
I have crashes in prod that I can't reproduce. The trace does not say where it crashes. After making some searches it looks like it could be related to notifyDataSetChanged(). It happens on Android 6 and 7.
I have 2 packages where I use listview:
A BroadcastReceiver to check the list of wifi APs: in this case the code collects all the APs, adds them in a List and calls notifyDataSetChanged:
@Override
public void onReceive(Context context, Intent intent) {
if (wifiManager != null) {
if (wifiManager.isWifiEnabled()) {
List<ScanResult> listeScan = wifiManager.getScanResults();
listeWifiItem.clear();
for (ScanResult scanResult : listeScan) {
WifiItem item = new WifiItem();
item.setAdresseMac(scanResult.BSSID);
item.setAPName(scanResult.SSID);
item.setForceSignal(scanResult.level);
listeWifiItem.add(item);
}
wifiAdapter.notifyDataSetChanged();
} else {
Toast.makeText(context, "You must activate the WiFi", Toast.LENGTH_SHORT);
}
}
}
And an async task in which I call notifyDataSetChanged in the method onPostExecute:
@Override
protected void onPostExecute(Void result) {
iPadaptater.notifyDataSetChanged();
super.onPostExecute(result);
}
Is there an issue with the way I use notifyDataSetChanged? In the async task, if the user moves to another function, I thought it was safer to do it
Do you think the crash is related to notifyDataSetChanged?
Do you see another important info from the traces?
Here are the 2 types of traces:
-type 1:
java.lang.IllegalStateException:
at android.widget.ListView.layoutChildren (ListView.java:1747)
at android.widget.AbsListView$CheckForTap.run (AbsListView.java:4728)
at android.os.Handler.handleCallback (Handler.java:751)
at android.os.Handler.dispatchMessage (Handler.java:95)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6776)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1410)
-type 2:
java.lang.IllegalStateException:
at android.widget.ListView.layoutChildren (ListView.java:1623)
at android.widget.AbsListView.onTouchUp (AbsListView.java:4210)
at android.widget.AbsListView.onTouchEvent (AbsListView.java:3969)
at android.view.View.dispatchTouchEvent (View.java:9957)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2705)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2386)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2400)
at com.android.internal.policy.DecorView.superDispatchTouchEvent (DecorView.java:416)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent (PhoneWindow.java:1837)
at android.app.Activity.dispatchTouchEvent (Activity.java:3154)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent (WindowCallbackWrapper.java:63)
at com.android.internal.policy.DecorView.dispatchTouchEvent (DecorView.java:378)
at android.view.View.dispatchPointerEvent (View.java:10177)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent (ViewRootImpl.java:4634)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4502)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3953)
at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:4006)
at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3972)
at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3980)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3953)
at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:4006)
at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3972)
at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:4101)
at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3980)
at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:4158)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3953)
at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:4006)
at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3972)
at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3980)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3953)
at android.view.ViewRootImpl.deliverInputEvent (ViewRootImpl.java:6443)
at android.view.ViewRootImpl.doProcessInputEvents (ViewRootImpl.java:6417)
at android.view.ViewRootImpl.enqueueInputEvent (ViewRootImpl.java:6378)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent (ViewRootImpl.java:6577)
at android.view.InputEventReceiver.dispatchInputEvent (InputEventReceiver.java:185)
at android.os.MessageQueue.nativePollOnce (Native Method)
at android.os.MessageQueue.next (MessageQueue.java:323)
at android.os.Looper.loop (Looper.java:136)
at android.app.ActivityThread.main (ActivityThread.java:6311)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:872)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:762)
Upvotes: 11
Views: 2789
Reputation: 62831
Looking at the source code for API 25, the only place that an illegal state exception is thrown is in LayoutChildren()
of ListView
. The exception states in the code:
Extract from ListView$LayoutChildren
:
// Handle the empty set by removing all views that are visible
// and calling it a day
if (mItemCount == 0) {
resetList();
invokeOnItemScrollListener();
return;
} 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() + ")]");
}
This indicates that there is a mismatch between the internal item count maintained by ListView
and the item count maintained by the adapter. It looks like LayoutChildren
is probably being called from the CheckforTap
inner class of AbsListView
:
Extract from AbsListView$CheckforTap
:
if (!mDataChanged) {
final float[] point = mTmpPoint;
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
child.drawableHotspotChanged(point[0], point[1]);
child.setPressed(true);
setPressed(true);
layoutChildren();
// ... and it continues...
This is probably why others are asking if you are changing the data set from a non-UI thread. It also seems to point to a potential issue with how you are calling notifyDataSetChanged()
.
Assuming that you are using the UI thread for the changes and without knowing more about the internal workings of your app, I can just suggest that you not separate the call to notifyDataSetChanged()
from the changes that you are making. You appear to do so from your responses.
Upvotes: 8
Reputation: 2079
You need to add items to your listeWifiItem
and called notifyDataSetChanged()
in the UI thread.
@Override
protected void onPostExecute (Void result){
super.onPostExecute(result);
runOnUiThread(new Runnable() {
@Override
public void run() {
listeWifiItem.clear();
for (ScanResult scanResult : listeScan) {
WifiItem item = new WifiItem();
item.setAdresseMac(scanResult.BSSID);
item.setAPName(scanResult.SSID);
item.setForceSignal(scanResult.level);
listeWifiItem.add(item);
}
wifiAdapter.notifyDataSetChanged();
}
});
}
Upvotes: 0