Вступление: Знакомая боль каждого Android-разработчика
Вы запускаете Build → Clean Project в Android Studio, надеясь на быстрое перестроение проекта, но вместо этого видите красный текст в окне Build:
Error: Execution failed for task ':app:clean'.
> Unable to delete file: C:\Users\User\AndroidStudioProjects\MyApp\app\build\...\some_file.dex
Знакомо? Если вы пишете на Kotlin под Android, эта ошибка может возникать с пугающей регулярностью, особенно на Windows. Она не только замедляет разработку, но и заставляет искать «магические» решения вроде перезагрузки компьютера.
В этой статье мы разберем почему возникает ошибка Unable to delete file, как её исправить раз и навсегда, и покажем примеры кода на Kotlin для автоматизации решения. К концу статьи вы сможете не только чинить проблему, но и предотвращать её.
Начнём с причин, затем перейдём к решениям — от простых до продвинутых.
Почему Gradle не может удалить файл?
Ошибка Unable to delete file при выполнении задачи :app:clean возникает, когда Gradle пытается очистить папку build/, но не может удалить один или несколько файлов. Основные причины:
- Файл заблокирован другим процессом — чаще всего это антивирус, Windows Search Indexer, или сама Android Studio.
- Долгие пути (Windows) — если путь к файлу превышает 260 символов, Windows может отказаться его удалять.
- Незакрытые потоки ввода-вывода — ваше приложение или библиотека могут держать файл открытым.
- Конфликт с Instant Run / Apply Changes — старые версии Android Studio могли блокировать файлы.
Чаще всего проблема проявляется на Windows, но может встречаться и на macOS/Linux при использовании SMB-шары или Docker.
Быстрые решения (на 5 минут)
Прежде чем лезть в код, попробуйте эти простые шаги. Они помогают в 80% случаев.
1. Закройте Android Studio и удалите вручную
Самый надёжный способ — закрыть IDE и вручную удалить папку app/build. Если файл не удаляется, используйте Unlocker (Windows) или команду lsof (macOS/Linux), чтобы найти блокирующий процесс.
2. Используйте терминал вместо GUI
Иногда Android Studio «зависает» на задаче clean. Выполните команду в терминале:
./gradlew clean --no-daemon
Флаг --no-daemon запускает Gradle без фонового процесса, что часто решает проблему блокировки.
3. Отключите Instant Run / Apply Changes
В Android Studio 3.x и старше Instant Run мог блокировать файлы. Отключите его:
- Перейдите в
File → Settings → Build, Execution, Deployment → Instant Run. - Снимите галочку
Enable Instant Run.
В новых версиях (Arctic Fox+) эта опция заменена на Apply Changes, но проблема встречается реже.
Продвинутые решения: автоматизация на Kotlin
Если быстрые методы не помогли, пора автоматизировать процесс. Мы напишем небольшой скрипт на Kotlin, который будет запускаться перед задачей clean и принудительно закрывать блокирующие процессы.
4. Используем Kotlin DSL в Gradle для кастомной задачи
Добавьте в файл build.gradle.kts (уровня модуля) следующий код:
// build.gradle.kts (app level)
tasks.register<Exec>("killFileLocks") {
description = "Закрывает процессы, блокирующие файлы в папке build"
commandLine("cmd", "/c", "taskkill /f /im java.exe 2>nul || exit 0")
// Для macOS/Linux замените на:
// commandLine("sh", "-c", "lsof -ti:8080 | xargs kill -9 2>/dev/null || true")
}
tasks.named("clean") {
dependsOn("killFileLocks")
}
Этот скрипт перед очисткой убивает процессы Java (которые могут держать файлы). Для macOS/Linux нужно адаптировать команду под lsof или fuser.
Важно: Убивать все Java-процессы — не лучшая практика. Лучше уточнить PID конкретного файла:
// Более точный вариант для Windows
commandLine("cmd", "/c", "for /f \"tokens=5\" %a in ('netstat -aon ^| find \"8080\"') do taskkill /f /pid %a 2>nul || exit 0")
5. Используем Kotlin-скрипт для очистки длинных путей
Проблема длинных путей (>260 символов) — бич Windows. Решение — использовать \\?\ префикс или сократить имена папок. Напишем Gradle-задачу на Kotlin, которая переименовывает проблемные файлы:
// build.gradle.kts (root level)
tasks.register("cleanLongPaths") {
doLast {
val buildDir = file("app/build")
if (buildDir.exists()) {
buildDir.walkTopDown().forEach { file ->
if (file.isFile && file.path.length > 250) {
println("Укорачиваю путь: ${file.path}")
// Переименовываем в короткое имя
val newName = file.parentFile.resolve("_" + file.name.takeLast(50))
file.renameTo(newName)
}
}
}
}
}
tasks.named("clean") {
dependsOn("cleanLongPaths")
}
Этот код обходит все файлы в app/build и укорачивает имена тех, чей полный путь превышает 250 символов. После этого Gradle сможет их удалить.
6. Используем Kotlin для проверки блокировок (Windows)
Напишем небольшую утилиту на Kotlin, которая использует handle.exe от Sysinternals для поиска блокировщика:
// KillLocker.kt
import java.io.File
fun main() {
val buildDir = File("app/build")
val lockFile = File(buildDir, "some_locked_file.dex") // замените на актуальный
if (lockFile.exists()) {
val process = ProcessBuilder("cmd", "/c", "handle.exe", "-accepteula", lockFile.absolutePath)
.redirectErrorStream(true)
.start()
val output = process.inputStream.bufferedReader().readText()
println("Блокирующие процессы: $output")
// Здесь можно добавить парсинг PID и taskkill
}
}
Для работы скачайте handle.exe и положите в PATH.
Профилактика: как избежать ошибки в будущем
Лучшее лечение — профилактика. Вот несколько советов, которые уменьшат вероятность появления ошибки Unable to delete file:
- Об