Установка requestOrientation приводит к сбою

Я пытаюсь протестировать приложение Android, созданное с помощью Kotlin и Jetpack, на предмет изменений конфигурации, таких как переключение в режим LANDSCAPE/PORTRAIT. Когда я устанавливаю ориентацию «Пейзаж/Портрет», я вижу сбой. Ниже приведен простой код AndroidTest. Может ли кто-нибудь помочь мне исправить это?

package com.example.sandbox
import android.content.pm.ActivityInfo
import androidx.compose.ui.test.ComposeTimeoutException
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.core.app.ActivityScenario
import org.junit.Rule
import org.junit.Test

class MainActivityTest {
    @get:Rule
    val rule = createAndroidComposeRule<MainActivity>()
    private lateinit var scenario: ActivityScenario<MainActivity>

    @Test
    fun sandboxTest() {
        scenario = ActivityScenario.launch(MainActivity::class.java)
        rule.waitForIdle()
        rule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            SandBoxTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!"
    )
}

Крушение:

java.lang.NullPointerException: Cannot run onActivity since Activity has been destroyed already
at androidx.test.internal.util.Checks.checkNotNull(Checks.java:50)
at androidx.test.core.app.ActivityScenario.lambda$onActivity$2$androidx-test-core-app-ActivityScenario(ActivityScenario.java:792)
at androidx.test.core.app.ActivityScenario$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:487)
at java.util.concurrent.FutureTask.run(FutureTask.java:264)
at android.app.Instrumentation$SyncRunnable.run(Instrumentation.java:2497)
at android.os.Handler.handleCallback(Handler.java:984)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loopOnce(Looper.java:238)
at android.os.Looper.loop(Looper.java:357)
at android.app.ActivityThread.main(ActivityThread.java:8149)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)


0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема, с которой вы столкнулись, скорее всего, связана со временем изменения ориентации. Когда вы устанавливаете запрошенную ориентацию, действие создается заново, что может привести к сбою, который вы видите. Вот несколько шагов, которые помогут вам это исправить:

Используйте ActivityScenario непосредственно в правиле тестирования. Вместо создания и управления ActivityScenario вручную используйте его в правиле тестирования Compose. Это обеспечивает правильную синхронизацию и управление жизненным циклом.

Подождите, пока действие станет бездействующим после изменения ориентации. Убедитесь, что тест ожидает, пока действие станет бездействующим после изменения ориентации.

Вот как вы можете обновить свой тестовый код:

class MainActivityTest {
    @get:Rule
    val rule = createAndroidComposeRule<MainActivity>()

    @Test
    fun sandboxTest() {
        // Launch the activity
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        // Wait for the activity to be idle
        rule.waitForIdle()

        // Change the orientation to landscape
        scenario.onActivity { activity ->
            activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        }

        // Wait for the activity to be idle again
        rule.waitForIdle()
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                Greeting(
                    name = "Android",
                    modifier = Modifier.padding(innerPadding)
                )
            }
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!"
    )
}

В этом обновленном коде:

  • ActivityScenario используется в правиле теста Compose, которое помогает управлять жизненным циклом и обеспечивает синхронизацию.
  • Изменение ориентации выполняется внутри лямбды onActivity, гарантируя, что это произойдет в основном потоке.
  • Тест ожидает бездействия активности до и после изменения ориентации, чтобы обеспечить правильную синхронизацию.

Это должно помочь предотвратить исключение NullPointerException, вызванное уничтожением активности во время выполнения теста.

Спасибо Юсеф!!! Это работает. Просто чтобы подтвердить мое понимание: правило выполняется в другом потоке, поэтому синхронизация не гарантируется. Это правильно?

Saral 24.07.2024 14:35

Не совсем. Правило выполняется в основном потоке, но не обрабатывает автоматически события восстановления активности, такие как изменения ориентации. При изменении ориентации действие уничтожается и создается заново, что при неправильном обращении может привести к проблемам с синхронизацией.

Youssef Islem 26.07.2024 11:46

Другие вопросы по теме