Странное исключение NullPointerException в Android SpinnerActivityTest

Недавно я работал с учебным пособием по тестированию активности на сайте разработчиков Android. Следуя пошаговому руководству до Добавление тестов пользовательского интерфейса — 1. вы получите этот фрагмент кода:

  package com.android.example.spinner.test;

import android.test.ActivityInstrumentationTestCase2;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;

import com.android.example.spinner.SpinnerActivity;

public class SpinnerActivityTest extends
        ActivityInstrumentationTestCase2<SpinnerActivity> {

    private SpinnerActivity mActivity;
    private Spinner mSpinner;
    private SpinnerAdapter mPlanetData;
    public static final int ADAPTER_COUNT = 9;
    public static final int INITIAL_POSITION = 0;

    public SpinnerActivityTest() {
        super("com.android.example.spinner", SpinnerActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        setActivityInitialTouchMode(false);
        mActivity = getActivity();
        mSpinner = (Spinner) mActivity
                .findViewById(com.android.example.spinner.R.id.Spinner01);
        mPlanetData = mSpinner.getAdapter();

    }

    public void testPreConditions() {
        assertTrue(mSpinner.getOnItemSelectedListener() != null);
        assertTrue(mPlanetData != null);
        assertEquals(mPlanetData.getCount(), ADAPTER_COUNT);
    }

    public void testSpinnerUI() {
        System.out.println("testSpinnerUI(): mSpinner = " + mSpinner);

        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                System.out.println("run(): mSpinner = " + mSpinner);
                mSpinner.requestFocus();
                mSpinner.setSelection(INITIAL_POSITION);
            }
        });
    }
}

Как это может быть?

Некоторые факты я уже выяснил:

  1. Этого не происходит при отладке TestApplication (даже без точек останова)
  2. Этого не происходит, когда вы отправляете поток пользовательского интерфейса в спящий режим на несколько миллисекунд.
  3. Когда вы регистрируете mSpinner непосредственно перед mActivity.runOnUiThread(), он НИКОГДА не равен нулю (даже если вы получаете исключение NullPointerException), но действительно, когда вы регистрируете его в Runnable.

// РЕДАКТИРОВАТЬ: добавлены System.outs + stacktrace:

07-30 19:20:08.001: INFO/ActivityManager(1362): Displayed com.android.example.spinner/.SpinnerActivity: +118ms
07-30 19:20:08.011: INFO/System.out(3180): testSpinnerUI(): mSpinner = android.widget.Spinner@40525150
07-30 19:20:08.071: DEBUG/dalvikvm(3180): GC_EXPLICIT freed 45K, 49% free 2754K/5379K, external 0K/0K, paused 44ms
07-30 19:20:08.071: INFO/System.out(3180): run(): mSpinner = null
07-30 19:20:08.081: DEBUG/AndroidRuntime(3180): Shutting down VM
07-30 19:20:08.081: WARN/dalvikvm(3180): threadid=1: thread exiting with uncaught exception (group=0x4001d5a0)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180): FATAL EXCEPTION: main
07-30 19:20:08.081: ERROR/AndroidRuntime(3180): java.lang.NullPointerException
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at com.android.example.spinner.test.SpinnerActivityTest$1.run(SpinnerActivityTest.java:46)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at android.os.Handler.handleCallback(Handler.java:587)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at android.os.Handler.dispatchMessage(Handler.java:92)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at android.os.Looper.loop(Looper.java:143)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at android.app.ActivityThread.main(ActivityThread.java:4196)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at java.lang.reflect.Method.invokeNative(Native Method)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at java.lang.reflect.Method.invoke(Method.java:507)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-30 19:20:08.081: ERROR/AndroidRuntime(3180):     at dalvik.system.NativeStart.main(Native Method)
07-30 19:20:08.121: DEBUG/dalvikvm(3180): GC_EXPLICIT freed 18K, 49% free 2756K/5379K, external 0K/0K, paused 43ms
07-30 19:20:08.131: WARN/ActivityManager(1362): Error in app com.android.example.spinner running instrumentation ComponentInfo{com.android.example.spinner.test/android.test.InstrumentationTestRunner}:
07-30 19:20:08.131: WARN/ActivityManager(1362):   java.lang.NullPointerException
07-30 19:20:08.131: WARN/ActivityManager(1362):   java.lang.NullPointerException

person Johannes Staehlin    schedule 30.07.2012    source источник
comment
включить трассировку стека.   -  person Jeffrey Blattman    schedule 30.07.2012
comment
Похоже на некоторые проблемы с параллелизмом, т.е. mSpinner исчезает к тому времени, когда run() до него доберется. Может быть, попробуйте что-нибудь с wait( ) и notify(), чтобы убедиться, что функция testSpinnerUI() не вернется до тех пор, пока Runnable не завершится?   -  person ikh    schedule 30.07.2012
comment
Я включил трассировку стека. @ikh Я могу заставить это работать со временем ожидания и т. Д. Это больше о том, чтобы узнать, ПОЧЕМУ это иногда не работает с моим подходом.   -  person Johannes Staehlin    schedule 30.07.2012
comment
Вероятно, из-за вызова testPreConditions() попробуйте закомментировать его и посмотреть, изменится ли что-нибудь. Дополнительные обсуждения в этой теме.   -  person yorkw    schedule 31.07.2012