4

我试图在我的代码中找到一些难以重现的问题。我使用了猴子工具。但在这里,问题似乎出在 Android 微调器内部。这是 API/框架问题吗?我再次尝试了相同的结果。

// CRASH: com.panguso.mobile.client (pid 11171)
// Short Msg: java.lang.NullPointerException
// Long Msg: java.lang.NullPointerException
// Build Label: google/soju/crespo:4.1.1/JRO03E/403059:user/release-keys
// Build Changelist: 403059
// Build Time: 1342214487000
// java.lang.NullPointerException
//  at android.widget.Spinner$DialogPopup.dismiss(Spinner.java:828)
//  at android.widget.Spinner$DialogPopup.onClick(Spinner.java:862)
//  at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:924)
//  at android.widget.AdapterView.performItemClick(AdapterView.java:298)
//  at android.widget.AbsListView.performItemClick(AbsListView.java:1086)
//  at android.widget.AbsListView.onKeyUp(AbsListView.java:2996)
//  at android.widget.ListView.commonKey(ListView.java:2196)
//  at android.widget.ListView.onKeyUp(ListView.java:2051)
//  at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
//  at android.view.View.dispatchKeyEvent(View.java:7086)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1354)
//  at android.widget.ListView.dispatchKeyEvent(ListView.java:2026)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
//  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
//  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1892)
//  at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1369)
//  at android.app.Dialog.dispatchKeyEvent(Dialog.java:702)
//  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1819)
//  at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3575)
//  at android.view.ViewRootImpl.deliverKeyEvent(ViewRootImpl.java:3531)
//  at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3113)
//  at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4153)
//  at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4132)
//  at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4224)
//  at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)
//  at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
//  at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:163)
//  at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:4203)
//  at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:4243)
//  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
//  at android.view.Choreographer.doCallbacks(Choreographer.java:555)
//  at android.view.Choreographer.doFrame(Choreographer.java:523)
//  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
//  at android.os.Handler.handleCallback(Handler.java:615)
//  at android.os.Handler.dispatchMessage(Handler.java:92)
//  at android.os.Looper.loop(Looper.java:137)
//  at android.app.ActivityThread.main(ActivityThread.java:4745)
//  at java.lang.reflect.Method.invokeNative(Native Method)
//  at java.lang.reflect.Method.invoke(Method.java:511)
//  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
//  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
//  at dalvik.system.NativeStart.main(Native Method)
// 
** Monkey aborted due to error.
Events injected: 1801
:Sending rotation degree=0, persist=false
:Dropped: keys=5 pointers=0 trackballs=0 flips=0 rotations=0
## Network stats: elapsed time=17738ms (0ms mobile, 17738ms wifi, 0ms not connected)
** System appears to have crashed at event 1801 of 10000 using seed 0
4

2 回答 2

4

您发布的堆栈跟踪没有提及您的代码中的任何类,这意味着这个问题至少不是由您直接引起的。

很少分析堆栈跟踪,从下到上,因为这是代码所采用的路径(可以使用http://www.grepcode.com/?st=true找到该堆栈跟踪的源代码):

...
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)

Monkey 发出一个键事件,就像你可以用软/硬键盘产生的一样。

然后将关键事件转发到包含您的应用内容和 ActionBar的DecorView 。DecorView事件转发到Dialog.

...
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1819)
at android.app.Dialog.dispatchKeyEvent(Dialog.java:702)

从这里通过视图层次结构,直到它到达ListView

at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1358)
at android.widget.ListView.dispatchKeyEvent(ListView.java:2026)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1354)
at android.view.View.dispatchKeyEvent(View.java:7086)
at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
at android.widget.ListView.onKeyUp(ListView.java:2051)
at android.widget.ListView.commonKey(ListView.java:2196)

ListView密钥转发到它的内容似乎是 aSpinner并且单击似乎触发 a.dismiss()到显示的Dialog(下拉列表)Spinner

at android.widget.AbsListView.onKeyUp(AbsListView.java:2996)
at android.widget.AbsListView.performItemClick(AbsListView.java:1086)
at android.widget.AdapterView.performItemClick(AdapterView.java:298)
at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:924)
at android.widget.Spinner$DialogPopup.onClick(Spinner.java:862)
at android.widget.Spinner$DialogPopup.dismiss(Spinner.java:828)

ASpinner和未提及您的代码的堆栈跟踪可能是系统 UI 的一部分,即 ActionBar。

例子
(来源:android.com

正如您在图片(取自此处)中看到的那样,下面有一个Spinner(显示日期)和Dialog一个ListView(日,周,...)。单击其中一项或微调器本身或外部某处将关闭对话框。

发生在以下NullPointerException代码中

public void dismiss() {
    mPopup.dismiss();  // Spinner.java:828
    mPopup = null;
}

mPopup很明显null。仅当弹出窗口之前未显示时才会发生这种情况,因为它已设置

public void show() {
    AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    if (mPrompt != null) {
        builder.setTitle(mPrompt);
    }
    mPopup = builder.setSingleChoiceItems(mListAdapter,
            getSelectedItemPosition(), this).show();
}

这是 API/框架问题吗?

我不知道,但猴子可以点击普通用户不一定能到达的东西,它可以点击用户无法点击的时间/速率。所以这个问题可能是由于API程序员没有预见/测试的方式点击造成的。是可能的,特别是因为堆栈跟踪是纯粹的框架,他们在其他地方而不是在这里检查nullmPopup

但除了这种可能性之外,它也可能与您的代码有关。也许您Spinner在布局(或 ActionBar)中添加了一个地方,并用它做一些不打算做的事情。而且我猜如果您不覆盖任何默认行为(尤其是关键事件处理),则框架不需要跳转到您的代码中并且会产生完全相同的堆栈跟踪。

于 2012-08-20T12:08:25.203 回答
0

不幸的是,我的所有 Android 设备都遇到了同样的问题。这个答案中有一个假设,我想对此进行扩展:

但这是奇怪的部分。堆栈跟踪似乎是单击 mPopup 的项目时,因为 DialogPopup 设置为其 OnClickListener。因此,此时 mPopup 不应为空。但是当 DialogPopup.dismiss() 它是空的。

这有什么可以解释的?这是我的假设。好吧,猴子通常很快。比实际用户更快。在第一个 DialogPopup.onClick() 真正解除 mPopup 之前,它可能能够单击两个项目。所以我们有两次调用 DialogPopup.dismiss(),第二次调用 NPE。

考虑到这个错误仍然存​​在,我认为是时候想出解决这个问题的解决方案了。以下是我也注意到的一些事情:

  1. 当我在模拟器上运行 monkeytester 时,它永远不会因为微调器空指针错误而崩溃。我相信这是真的,因为模拟器比 Android 设备慢很多,而且猴子测试器不能足够快地发出触摸事件以导致崩溃。

  2. 当使用带有猴子测试器的 Android 设备时,使用相同的种子几乎总是会导致相同的崩溃。这是有道理的,因为设备将为每个种子接收相同的伪随机触摸事件。我不再使用 0 种子,因为它几乎总是在我的几次触摸事件后导致崩溃。


解决方法

从我所做的所有搜索来看,这个问题没有真正的解决方案。但是您可以采取一些措施来避免崩溃。

  1. 使用模拟器进行持续时间超过几分钟的测试。模拟器的执行速度比实际设备慢很多,因此猴子测试器无法以足够快的速度发出命令以导致崩溃。如果您使用的是英特尔硬件加速器 (HAXM),我建议您创建一个不使用它的 AVD。确保启用快照并禁用 GPU 加速,这样您就不必永远等待 AVD 启动。

  2. 如果您想在实际设备上进行测试,请确保将--throttle <millis>参数传递给猴子测试器。这是文档中的定义:

    在事件之间插入固定延迟。您可以使用此选项来减慢猴子的速度。如果未指定,则没有延迟,并且事件会尽快生成。

这是使用的示例命令--throttle

adb -s shell monkey --throttle 200 -p com.your.package -v 500000

您应该尝试为您的设备找到最佳延迟。对于我的三星 Galaxy Nexus,延迟必须接近一秒才能防止崩溃。这不言而喻,但延迟的时间越长,完成猴子测试所需的时间就越长。这就是为什么找到一个设备不会崩溃但也不会超慢的值很重要的原因。

于 2014-01-14T04:41:01.113 回答