← Назад к вопросам

Как проверить параллельность выполнения

2.0 Middle🔥 62 комментариев
#Многопоточность и асинхронность#Тестирование

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проверка параллельности выполнения в Android

Проверка параллельности выполнения в Android — это комплексная задача, так как она затрагивает многопоточность, асинхронные операции и взаимодействие с UI-потоком. Вот основные подходы и инструменты для проверки.

Основные принципы параллельности в Android

  1. Главный поток (UI-поток) — обрабатывает обновления интерфейса и события пользователя. Должен оставаться отзывчивым.
  2. Фоновые потоки — используются для долгих операций (сеть, БД, вычисления). Создаются через Thread, ExecutorService, CoroutineDispatcher или RxJava Schedulers.
  3. Синхронизация — управление доступом к общим ресурсам из нескольких потоков (через synchronized, Atomic-классы, Mutex в Kotlin).

Методы проверки параллельности

1. Логирование и отладка

  • Добавление идентификаторов потоков в логи:
Log.d("Concurrency", "Действие в потоке: ${Thread.currentThread().name}")
  • Точки останова в Debugger с условиями по имени потока.

2. Инструменты Android Studio

  • Android Profiler → вкладка CPU → запись выполнения с детализацией по потокам.
  • Layout Inspector — проверка блокировок UI из-за долгих операций в главном потоке.

3. Написание юнит-тестов с проверкой потоков

Для JUnit-тестов используйте проверки на выполнение в фоновом потоке:

@Test
fun testBackgroundOperation() {
    var isMainThread = false
    val latch = CountDownLatch(1)
    
    GlobalScope.launch(Dispatchers.IO) {
        isMainThread = Looper.getMainLooper() == Looper.myLooper()
        latch.countDown()
    }
    
    latch.await(2, TimeUnit.SECONDS)
    assertFalse(isMainThread)
}

4. Интеграционные тесты с Espresso и Idling Resources

IdlingResource — механизм Espresso для ожидания завершения фоновых задач:

class MyIdlingResource : IdlingResource {
    override fun getName() = "MyBackgroundTask"
    override fun isIdleNow() = !isTaskRunning // Флаг завершения задачи
    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
        this.callback = callback
    }
    
    fun onTaskFinished() {
        callback?.onTransitionToIdle()
    }
}

5. Стресс-тестирование и детекция гонок данных

  • Повторение операций в циклах (1000+ итераций) для выявления редких состояний гонки.
  • Использование Collections.synchronizedList() или ConcurrentHashMap с последующей проверкой целостности данных.
  • Инструмент ThreadSanitizer (TSan) — доступен в нативных (C/C++) слоях.

6. Проверка корутин (Kotlin Coroutines)

Для корутин используйте тестовый диспетчер TestCoroutineDispatcher или runTest в kotlinx-coroutines-test:

@Test
fun testParallelCoroutines() = runTest {
    val dispatcher = StandardTestDispatcher(testScheduler)
    val results = mutableListOf<Int>()
    
    val job1 = launch(dispatcher) { results.add(1) }
    val job2 = launch(dispatcher) { results.add(2) }
    
    joinAll(job1, job2)
    assertEquals(listOf(1, 2), results) // Порядок может быть разным
}

7. Мониторинг deadlock и ANR

  • Анализ логов ANR (/data/anr/traces.txt) для обнаружения взаимных блокировок.
  • Использование StrictMode для детекции операций на UI-потоке:
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
    .detectNetwork()
    .detectDiskReads()
    .penaltyLog() // Или penaltyDeath() для тестов
    .build())

Типичные проблемы и их проверка

ПроблемаМетод проверки
Утечка потокаПроверка через LeakCanary + мониторинг количества потоков в Profiler.
Гонка данныхСтресс-тесты с увеличением нагрузки и проверкой атомарности.
Блокировка UIЗапуск долгой операции и проверка отклика интерфейса (Espresso + IdlingResource).
Некорректная отменаТестирование отмены Job в корутинах или Disposable в RxJava.

Рекомендации

  • Всегда документируйте предполагаемую модель параллельности для каждого компонента.
  • Пишите детерминированные тесты, используя тестовые диспетчеры для корутин или TestScheduler для RxJava.
  • Профилируйте приложение на слабых устройствах, где проблемы параллельности проявляются чаще.

Правильная проверка параллельности требует комбинации статического анализа (код-ревью), динамического тестирования (инструменты профилирования) и написания специализированных тестов, моделирующих конкурентный доступ.