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

Как позволить фоновому потоку остаться выжившим после поворота экрана в Android

2.0 Middle🔥 162 комментариев
#Android компоненты#Жизненный цикл и навигация#Многопоточность и асинхронность

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

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

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

Проблема фоновых потоков при повороте экрана

Основная проблема заключается в том, что при повороте экрана Android уничтожает текущую активность (Activity) и создает ее заново. Это приводит к потере ссылок на фоновые потоки, созданные в старой активности, что может вызвать утечки памяти и непредсказуемое поведение.

Решения для сохранения фоновых потоков

1. ViewModel + Coroutines (Рекомендуемый современный подход)

Используйте ViewModel, который переживает смену конфигурации, в сочетании с Kotlin Coroutines:

class MyViewModel : ViewModel() {
    private val viewModelScope = CoroutineScope(
        SupervisorJob() + Dispatchers.Default
    )
    
    fun startBackgroundWork() {
        viewModelScope.launch {
            // Долгая фоновая операция
            val result = withContext(Dispatchers.IO) {
                performLongRunningTask()
            }
            // Обработка результата
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel() // Автоматическая отмена при уничтожении ViewModel
    }
}

2. RetainedFragment (Fragment с setRetainInstance)

Специальный фрагмент, который сохраняется при повороте:

class RetainedWorkerFragment : Fragment() {
    private var workerThread: Thread? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance = true // Ключевая настройка!
    }
    
    fun startBackgroundTask(callback: (String) -> Unit) {
        workerThread = Thread {
            val result = performWork()
            activity?.runOnUiThread {
                callback(result)
            }
        }.apply { start() }
    }
    
    override fun onDestroy() {
        workerThread?.interrupt()
        super.onDestroy()
    }
}

3. Service для действительно независимых операций

Если задача должна выполняться независимо от жизненного цикла UI:

class BackgroundService : Service() {
    private val binder = LocalBinder()
    private var isRunning = false
    
    inner class LocalBinder : Binder() {
        fun getService(): BackgroundService = this@BackgroundService
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (!isRunning) {
            startBackgroundWork()
            isRunning = true
        }
        return START_STICKY
    }
    
    private fun startBackgroundWork() {
        Thread {
            while (isRunning) {
                // Выполнение фоновой работы
                Thread.sleep(1000)
            }
        }.start()
    }
    
    override fun onBind(intent: Intent?): IBinder = binder
    
    override fun onDestroy() {
        isRunning = false
        super.onDestroy()
    }
}

4. WorkManager для отложенных гарантированных задач

Для периодических или отложенных задач, которые должны быть выполнены гарантированно:

class BackgroundWorker(context: Context, params: WorkerParameters) 
    : Worker(context, params) {
    
    override fun doWork(): Result {
        return try {
            // Выполнение фоновой работы
            performTask()
            Result.success()
        } catch (e: Exception) {
            Result.retry() // или Result.failure()
        }
    }
}

// Запуск из Activity/Fragment
val workRequest = OneTimeWorkRequestBuilder<BackgroundWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .build()

WorkManager.getInstance(context).enqueue(workRequest)

Ключевые рекомендации и best practices

Архитектурные паттерны:

  • Используйте MVVM/MVI с ViewModel как основу
  • Разделяйте ответственность между компонентами
  • Используйте Dependency Injection (Dagger/Hilt) для управления зависимостями

Управление потоками:

  • Предпочитайте Coroutines над чистыми Thread
  • Используйте правильные Dispatchers: IO для операций ввода-вывода, Default для CPU-intensive задач
  • Всегда отменяйте корутины в onCleared() ViewModel

Обработка поворотов:

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // ViewModel сохранится при повороте
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        
        // Наблюдение за LiveData/StateFlow
        viewModel.resultData.observe(this) { result ->
            // Обновление UI с результатами
        }
    }
}

Важные предостережения:

  • Избегайте утечек памяти: не храните ссылки на Activity в фоновых потоках
  • Используйте WeakReference если необходимо передать контекст
  • Тестируйте смену конфигурации вручную и автоматически
  • Учитывайте состояние процесса: при нехватке памяти Android может убить весь процесс

Выбор конкретного подхода зависит от задачи: для операций, связанных с UI, используйте ViewModel + Coroutines, для независимых долгих операций - Service или WorkManager, а для простых случаев сохранения состояния - RetainedFragment.