← Назад к вопросам
Для чего нужен DisposableEffect?
2.3 Middle🔥 161 комментариев
#UI и вёрстка#Жизненный цикл и навигация
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
DisposableEffect в Jetpack Compose
DisposableEffect — это композиционный хук (side-effect) в Jetpack Compose, который выполняет очистку (cleanup) когда композиция переходит в другое состояние. Это аналог useEffect с cleanup функцией в React.
Проблема: Управление ресурсами
Без DisposableEffect (неправильно)
@Composable
fun LocationScreen(locationManager: LocationManager) {
// ❌ Проблема: listener добавляется каждый раз при перекомпозиции
locationManager.startListening { location ->
// Обновляем UI
}
// Но listener НИКОГДА не удаляется!
// Memory leak: множественные listeners скапливаются
}
Решение: DisposableEffect
@Composable
fun LocationScreen(locationManager: LocationManager) {
DisposableEffect(locationManager) {
// Эта блок выполняется при входе в композицию
val listener = { location: Location ->
println("Location: $location")
}
locationManager.startListening(listener)
// onDispose блок выполняется при выходе из композиции
onDispose {
locationManager.stopListening(listener)
println("Listener удалён!")
}
}
}
Жизненный цикл DisposableEffect
@Composable
fun EffectDemo() {
println("1. Начало композиции")
DisposableEffect(Unit) {
println("2. DisposableEffect выполняется")
// Можно здесь выделить ресурсы
val resource = acquireResource()
onDispose {
println("5. onDispose вызван!")
// Очистить ресурсы
resource.release()
}
}
println("3. Остальная композиция")
}
// Вывод:
// 1. Начало композиции
// 2. DisposableEffect выполняется
// 3. Остальная композиция
// [при выходе из композиции]
// 5. onDispose вызван!
Keys: Когда пересчитывать
@Composable
fun ChatScreen(userId: String) {
DisposableEffect(userId) { // ← Key!
println("Подключаемся к пользователю: $userId")
val connection = chatService.connect(userId)
onDispose {
println("Отключаемся от пользователя: $userId")
connection.close()
}
}
}
// Если userId изменится (например, с "user123" на "user456"):
// 1. Вызывается onDispose для старого userId
// 2. Затем заново выполняется блок с новым userId
Практические примеры
1. Подписка на события жизненного цикла
@Composable
fun LifecycleAwareScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> println("Screen visible")
Lifecycle.Event.ON_PAUSE -> println("Screen hidden")
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
2. Управление сенсорами (GPS, акселерометр)
@Composable
fun AccelerometerScreen(sensorManager: SensorManager) {
DisposableEffect(Unit) {
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
val listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
println("Acceleration: X=$x, Y=$y, Z=$z")
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
sensorManager.registerListener(listener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)
onDispose {
sensorManager.unregisterListener(listener)
}
}
}
3. Управление WebSocket соединением
@Composable
fun ChatScreen(roomId: String, webSocketManager: WebSocketManager) {
DisposableEffect(roomId) {
println("Подключаемся к room: $roomId")
val connection = webSocketManager.connect(roomId)
connection.onMessageReceived { message ->
println("Сообщение: $message")
}
onDispose {
println("Закрываем соединение с room: $roomId")
connection.close()
}
}
}
4. Управление таймерами
@Composable
fun TimerScreen() {
DisposableEffect(Unit) {
var elapsed = 0
val timer = Timer()
timer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
elapsed++
println("Прошло: $elapsed сек")
}
}, 0, 1000) // Каждую секунду
onDispose {
timer.cancel() // Остановить таймер!
}
}
}
5. Управление AppCompatDelegate для темы
@Composable
fun ThemeAwareScreen(isDarkMode: Boolean) {
DisposableEffect(isDarkMode) {
val previousMode = AppCompatDelegate.getDefaultNightMode()
val newMode = if (isDarkMode) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
AppCompatDelegate.setDefaultNightMode(newMode)
onDispose {
// Вернуть предыдущий режим
AppCompatDelegate.setDefaultNightMode(previousMode)
}
}
}
6. Регистрация BroadcastReceiver
@Composable
fun BatteryStatusScreen(context: Context) {
DisposableEffect(Unit) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) ?: 0
println("Батарея: $level%")
}
}
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
context.registerReceiver(receiver, filter)
onDispose {
context.unregisterReceiver(receiver)
}
}
}
DisposableEffect vs LaunchedEffect
LaunchedEffect — для корутин
@Composable
fun DataFetch(userId: String) {
LaunchedEffect(userId) {
// Запускается в viewModelScope
val user = repository.getUser(userId)
// Автоматически отменяется при изменении userId
}
}
DisposableEffect — для всего остального
@Composable
fun EventListener(eventBus: EventBus) {
DisposableEffect(eventBus) {
val listener = { event ->
println("Event: $event")
}
eventBus.subscribe(listener)
onDispose {
eventBus.unsubscribe(listener)
}
}
}
Best Practices
// 1. Всегда указывай keys!
// ❌ Плохо
DisposableEffect(Unit) { // Только один раз!
// ...
}
// ✅ Хорошо
DisposableEffect(userId) { // Пересчитает при изменении userId
// ...
}
// 2. Всегда очищай ресурсы в onDispose
DisposableEffect(Unit) {
val resource = acquireResource()
onDispose {
resource.close() // ОБЯЗАТЕЛЬНО!
}
}
// 3. Помни про memory leaks
DisposableEffect(Unit) {
listeners.add(myListener) // Добавили listener
onDispose {
listeners.remove(myListener) // УДАЛИ его!
}
}
// 4. Обработка исключений
DisposableEffect(Unit) {
try {
resource.initialize()
} catch (e: Exception) {
Log.e("DisposableEffect", "Ошибка", e)
}
onDispose {
try {
resource.cleanup()
} catch (e: Exception) {
Log.e("DisposableEffect", "Ошибка cleanup", e)
}
}
}
Сравнение с обычным Lifecycle
| Аспект | DisposableEffect | Lifecycle Observer |
|---|---|---|
| Удобство | Inline в composable | Отдельный класс |
| Реакция на изменения | По keys | По событиям |
| Очистка | onDispose блок | onDestroy |
| Memory leaks | Предотвращено | Нужна осторожность |
| Compose | ✅ Встроено | ❌ Старший API |
Вывод: DisposableEffect — это essential хук для управления ресурсами в Compose. Всегда используй его для подписок, слушателей и других ресурсов, требующих очистки.