2

我在 ICS 设备上遇到了非常奇怪的行为。我在布局中有一个按钮,然后单击 Listener,如下所示。

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button b = (Button) findViewById(R.id.button1);
    b.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
        // To just demonstrate weird behavior, i am throwing run-time exception 
         throw new RuntimeException("testing weird behavior on ICS");

         //String s = null;   s.length(); You can try this , application does not crash.
        } 
    });
}

在正常情况下,这应该会使应用程序崩溃,但令人惊讶的是,我可以在 logcat 中看到如下所示的崩溃,但应用程序不会崩溃。而是我看到

Failed to handle callback; interface not implemented, callback:android.view.View$PerformClick@40d80cc8
java.lang.RuntimeException: testing weird  behaviour
     at com.example.sampleproject.MainActivity$1.onClick(MainActivity.java:21)
     at android.view.View.performClick(View.java:3538)
     at android.view.View$PerformClick.run(View.java:14330)
     at android.os.Handler.handleCallback(Handler.java:608)
     at android.os.Handler.dispatchMessage(Handler.java:92)
     at android.os.Looper.loop(Looper.java:154)
     at android.app.ActivityThread.main(ActivityThread.java:4977)
     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:784)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
     at dalvik.system.NativeStart.main(Native Method)

任何想法为什么我会遇到这种奇怪的行为,任何帮助表示赞赏。

根据@Jen 的建议,我尝试了 b.performClick(); ,并且应用程序显示正常行为,但在 onClickListener 中的行为仍然相同。

我在这里提交的错误是链接 http://code.google.com/p/android/issues/detail?id=35517&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

4

1 回答 1

0

您是否正在检查按钮b是否为空?正如logcat所说java.lang.NullPointerException,奇怪的是你遇到了这个问题,我通常是这样的:

_btnFoo = (Button)findViewById(R.id.btnFoo);
if (_btnFoo != null){
   _btnFoo.setOnClickListener(new OnClickListener(){
      @Override
      public void onClick(View v){
         ....
      }
   });
}

它的空指针异常像拇指酸痛一样突出,所以周围没有初始化一些东西。

编辑: 由于我的回答不够充分,我想挖掘来源,取自 ICS 4.0.3 repo,这是在此处找到的代码的一部分,位于第 14103 行frameworks/base/core/java/android/view/View.java

private final class PerformClick implements Runnable {
    public void run() {
        performClick();
    }
}

该方法的核心在同一来源的第 3505 行中找到:

public boolean performClick() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            return true;
        }

        return false;
    }

我的猜测是Runnable内部是有问题的,PerformClick因为它似乎是跨线程的,没有任何暗示runOnUiThread或类似的保护措施。

我会在上一句中强调这一点——我会放一个开放式问号,并在单词的意义上松散地使用它,而不是从字面上理解。

Jen 的评论被证明是正确的,如下所示,取自第 3010 行:

case R.styleable.View_onClick:
    if (context.isRestricted()) {
        throw new IllegalStateException("The android:onClick attribute cannot "
                + "be used within a restricted context");
    }

    final String handlerName = a.getString(attr);
    if (handlerName != null) {
        setOnClickListener(new OnClickListener() {
            private Method mHandler;

            public void onClick(View v) {
                if (mHandler == null) {
                    try {
                        mHandler = getContext().getClass().getMethod(handlerName,
                                View.class);
                    } catch (NoSuchMethodException e) {
                        int id = getId();
                        String idText = id == NO_ID ? "" : " with id '"
                                + getContext().getResources().getResourceEntryName(
                                    id) + "'";
                        throw new IllegalStateException("Could not find a method " +
                                handlerName + "(View) in the activity "
                                + getContext().getClass() + " for onClick handler"
                                + " on view " + View.this.getClass() + idText, e);
                    }
                }

                try {
                    mHandler.invoke(getContext(), View.this);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("Could not execute non "
                            + "public method of the activity", e);
                } catch (InvocationTargetException e) {
                    throw new IllegalStateException("Could not execute "
                            + "method of the activity", e);
                }
            }
        });
    }
    break;

在第 3452 行,该setOnClickListener方法是注册回调的地方,如下所示:

public void setOnClickListener(OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

OP 的问题的答案是框架中存在一个错误,正如 Jens 简洁地说的那样,至少在错误处理方面存在差异。通常不应该抛出异常onClick- 但我可以想象,如果在点击处理程序中执行大量工作的应用程序没有报告强制关闭,这将使“在野外”追踪错误变得更加困难.

于 2012-07-25T20:16:29.950 回答