【问题标题】:Difference between runOnUiThread() method and @UiThreadTest annotationrunOnUiThread() 方法和@UiThreadTest 注解的区别
【发布时间】:2013-10-25 23:34:42
【问题描述】:

如题,谁能解释一下runOnUiThread()方法和@UiThreadTest注解的区别?我一直在阅读使用两者的 Android 测试教程 (http://developer.android.com/tools/testing/activity_test.html)。它指出:

测试应用程序中与被测应用程序的视图交互的代码必须在主应用程序的线程(也称为 UI 线程)中运行。为此,您使用 Activity.runOnUiThread() 方法

和:

@UiThreadTest 注释告诉 Android 构建此方法,以便它在 UI 线程上运行。这允许该方法在被测应用程序中更改微调器小部件的状态。

对于runOnUi()方法,有问题的代码是

public void testASpinnerUI()
{
    mActivity.runOnUiThread(
            new Runnable()
            {
                @Override
                public void run()
                {
                    mSpinner.requestFocus();
                    mSpinner.setSelection(INITIAL_POSITION);
                }// end of run
            } // end of runnable
        ); //end of runOnUiThread

    this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    for (int i = 0; i < TEST_POSITION; i++)
    {
        this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
    }
    this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);

    mPos = mSpinner.getSelectedItemPosition();
    mSelection = (String) mSpinner.getItemAtPosition(mPos);

    TextView resultView = (TextView) mActivity.findViewById(com.android.example.spinner.R.id.SpinnerResult);

    String resultText = (String) resultView.getText();
    assertEquals(resultText, mSelection);
}

对于@UiThreadTest 注解:

@UiThreadTest
public void testStatePause()
{
    Instrumentation mInstr = this.getInstrumentation();
    mActivity.setSpinnerPosition(TEST_STATE_PAUSE_POSITION);
    mActivity.setSpinnerSelection(TEST_STATE_PAUSE_SELECTION);

    mInstr.callActivityOnPause(mActivity);

    mActivity.setSpinnerPosition(0);
    mActivity.setSpinnerSelection("");

    mInstr.callActivityOnResume(mActivity);

    int currentPosition = mActivity.getSpinnerPosition();
    String currentSelection = mActivity.getSpinnerSelection();

    assertEquals(TEST_STATE_PAUSE_POSITION, currentPosition);
    assertEquals(TEST_STATE_PAUSE_SELECTION, currentSelection);     
}

它们似乎是可互换的,因为我可以从带注释的测试中删除注释并将其内容包含在 runOnUiThread() 方法中并且它通过了。同样,我可以从另一个测试中删除 runOnUiThread() 方法并添加 @UiThreadTest 注释,它通过了。

那么有什么区别呢?

此外,本教程还包括另一个测试:

public void testStateDestroy()
{
    mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
    mActivity.setSpinnerSelection(TEST_STATE_DESTROY_SELECTION);

    mActivity.finish();
    mActivity = getActivity();

    int currentPosition = mActivity.getSpinnerPosition();
    String currentSelection = mActivity.getSpinnerSelection();

    assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
    assertEquals(TEST_STATE_DESTROY_SELECTION, currentSelection);
}

此测试也与活动交互,但不需要@UiThreadTest 注释或runOnUiThread() 方法。这是为什么呢?

【问题讨论】:

  • 第三个测试肯定看起来不是线程安全的。
  • 它并没有失败。其他测试没有 runOnUi 方法或 @UiThreadTest 注释
  • 对不起,重新阅读测试后,不,这不是比赛。设置和读取都发生在测试线程中(假设 UI 线程没有触及这些值)。因此,set/get 对不是竞争条件。 finish() 调用可能是,因为我们没有等待它,但这会使测试无效,而不是失败(即,它没有测试正确的东西,尽管结果可能仍然是 success)。
  • 当我使用@UiThreadTest 而不是@Test 时,Android Studio 会将测试名称设为灰色,就好像没有人调用它一样。这些天必须添加@Test

标签: android unit-testing junit


【解决方案1】:

区别在于语义和副作用。

首先,@UiThreadTest 的存在会导致创建活动,如果它还没有 by calling getActivity()

然后,在InstrumentatinTestCase 中,它使用getInstrumentation().runOnMainSync() 运行完整的测试。

getInstrumentation().runOnMainSync()Activity.runOnUiThread() 之间的区别在于前者等待调用完成(运行完整测试时需要,或者,你知道,在测试中调用事物时需要),而后者不需要。

除此之外,他们发布到不同的Handlers(runOnMainSync 使用来自ActivityThread 的那个,而Activity 实例有自己的)但这无关紧要,因为他们被安排在同一个MessageQueue.

【讨论】:

  • 谢谢。那么你什么时候会使用其中一个呢?在教程中(添加到问题的链接)是什么让他们选择了?
  • 嗯... testASpinnerUI 测试我用@UiThreadTest 替换了runOnUi 现在失败了。我确定它早些时候通过了......也许是一些时间问题
  • testASpinnerUI 的 runOnUi 版本中有一个竞态条件。 runOn 调用只是调度调用,它不会等待它被执行。你需要在那里使用runOnMainSync。 @UiThreadTest 版本的 testASpinnerUi 应该可以正常工作(我认为;sendKeys 应该是安全的,但我不是 100% 确定,即使文档说没关系)。一般来说,你不应该直接使用runOnUiThread,因为这不会等待runnable被执行,你需要runOnMainSync。也就是说,我只使用Robotium。这很 hacky(睡了很多)但是很管用。
  • 此外,Google 正在使用他们的内部库,称为 Espresso,它使用 fest 断言并为您处理同步。不过,他们还没有发布它,但请留意。
  • 我试图通过在 runnable 中添加一个 Thread.sleep(10000) 来验证你所说的关于 runOnUi 和 runOnMainSync 之间的区别。在这两种情况下,测试都通过了在其余测试之前执行的可运行代码。因此,在这两种情况下,runnable 似乎都被允许完成。这很令人困惑——我是不是误会了什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
  • 2012-05-17
相关资源
最近更新 更多