Как позволить фоновому потоку остаться выжившим после поворота экрана в Android
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема фоновых потоков при повороте экрана
Основная проблема заключается в том, что при повороте экрана 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.