【问题标题】:Crash: java.lang.IllegalStateException in android.widget.ListView.layoutChildren崩溃:android.widget.ListView.layoutChildren 中的 java.lang.IllegalStateException
【发布时间】:2017-10-08 08:18:53
【问题描述】:

我在 prod 中发生了无法重现的崩溃。跟踪没有说明它在哪里崩溃。在进行了一些搜索之后,它看起来可能与 notifyDataSetChanged() 有关。 它发生在 Android 6 和 7 上。

我有 2 个使用 listview 的包:

用于检查 wifi AP 列表的 BroadcastReceiver:在这种情况下,代码会收集所有 AP,将它们添加到 List 中并调用 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);
            }
        }
    }

还有一个异步任务,我在 onPostExecute 方法中调用 notifyDataSetChanged:

@Override
protected void onPostExecute(Void result) {
    iPadaptater.notifyDataSetChanged();
    super.onPostExecute(result);
}

我使用 notifyDataSetChanged 的​​方式有问题吗?在异步任务中,如果用户移动到另一个函数,我认为这样做更安全

您认为崩溃与 notifyDataSetChanged 有关吗?

您是否从痕迹中看到了其他重要信息?

以下是两种类型的痕迹:

-类型 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)

-类型 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)

【问题讨论】:

  • 你是否在后台线程上执行listeWifiItem.add(item)?
  • 我的应用程序使用多个列表视图,并且由于跟踪没有说太多,我试图总结我使用列表视图的 2 个场景。我已经更新了上面关于 wifi 的代码。为了回答您的问题,wifi 的这种特殊情况不在后台线程中运行。它是一个广播接收器。我应该寻找什么?在我使用异步任务的场景中,我通常在 OnProgressUpdate 或 onPostExecute 中添加所有项目,然后调用 notifyDataSetChanged。我很少崩溃,但仍然很烦人。

标签: android listview crash


【解决方案1】:

查看 API 25 的源代码,唯一抛出非法状态异常的地方是 LayoutChildren() 的 ListView。代码中的异常状态:

从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() + ")]");
}

这表明ListView 维护的内部项目计数与适配器维护的项目计数不匹配。看起来LayoutChildren 可能是从AbsListView 的CheckforTap 内部类调用的:

从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...

这可能是其他人询问您是否从非 UI 线程更改数据集的原因。这似乎还指出了您如何调用notifyDataSetChanged() 的潜在问题。

假设您使用 UI 线程进行更改并且不了解应用程序的内部工作原理,我建议您不要将对 notifyDataSetChanged() 的调用与所做的更改分开。从您的回复看来,您是这样做的。

【讨论】:

  • 我在跟踪中没有收到消息“适配器的内容......”是否正常?我怎么可能没有得到关于它在哪里坠毁的信息?你从哪里得到的 src 代码(我还有几次崩溃,我在跟踪中没有信息;))。非常感谢您提供的信息。我会再检查一次我的代码,并会尽快回复您。
  • @narb 我无法解释为什么您没有收到消息,但这是唯一引发异常的地方,并且非常接近错误消息中的行号。我有信心这是您看到的错误。至于源代码,请在源代码中的“ListView”上键入 ctrl-B (Windows)。这将带你反编译字节码,然后在顶部单击“下载源”或类似的东西。你也可以关注these instructions。
  • @narb 我很好奇您是否在代码中发现了问题。
  • 只有时间会证明一切,因为它只发生在应用程序的订阅者身上 :) 但我发现了一些对 notifyDataSetChanged() 的奇怪调用。当你在它坠毁的痕迹中没有任何迹象时,它会很痛苦。我仍然不明白为什么android没有提供更多信息。我还有一对这样的:tgkill 和 getColorStateList 问题。编写高度多处理的应用程序并非易事。
  • 嗨,Cheticamp,很抱歉打扰您。我的问题还在。总结一下:我从不删除条目来重新创建它们。我所做的是创建列表视图,如果我需要更改某些内容,我清除()列表项,然后重新创建列表视图。所以我的理解是:第一次创建列表视图我不必调用 notifydatasetchanged。我只做 item.setxxx() listitem.add() 和 listview.setadapter(adapter)。我必须调用 notifydatasetchanged 的​​唯一情况是在 listitem.clear() 之后。同意吗?
【解决方案2】:

您需要将项目添加到您的 listeWifiItem 并在 UI 线程中调用 notifyDataSetChanged()。

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

【讨论】:

  • 我很抱歉我的电子邮件并不完全清楚,但恕我直言,这不是问题。见对阿兹别克人的评论
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 2023-03-08
  • 1970-01-01
  • 2021-05-14
相关资源
最近更新 更多