Aller au contenu

SDK Android, émulateurs et Gradle

La catégorie android (alias : gradle, emulators) couvre la chaîne d'outils Android d'un développeur React Native ou Expo : les émulateurs (AVD) et leurs snapshots, les paquets du SDK, le dossier utilisateur de Gradle, les caches et réglages d'Android Studio, et les JDK installés.

Les dossiers propres à chaque projet, comme android/build, android/app/build, android/.gradle et .cxx, sont des artefacts de projet, traités dans Projets React Native.

Fenêtre de terminal
./gradlew --stop # depuis n'importe quel projet Android : arrêter d'abord les démons Gradle
lu-cleaner scan -c android # ce qui prend de la place ; rien n'est modifié
lu-cleaner clean -c android # sélecteur : rien n'est sélectionné, appuyez sur a pour les éléments recommandés
lu-cleaner devices # AVD et images système à côté des simulateurs iOS (a pour la sélection intelligente)

Un scan typique (tailles données à titre d'exemple) :

Android 21.2 GB · 9 items · ★ 7.60 GB recommended NAME SIZE AGE RISK RECO AVD Pixel 8 · API 35 · arm64-v8a 9.80 GB 1d caution System image · API 33 · Google APIs · x86_64 3.90 GB — moderate ★ NDK 27.1.12297006 2.60 GB — moderate NDK 26.1.10909125 2.40 GB — moderate ★ Quick Boot snapshots · Pixel 8 1.90 GB 1d moderate Android Studio caches · 2024.1 1.10 GB 7mo safe ★ Gradle 8.13 caches 980 MB now moderate ! Gradle daemon running — stop it first (./gradlew --stop or pkill -f GradleDaemon) Gradle 8.3 distribution (bin) 196 MB 9mo moderate ★ JDK 11.0.24 (Zulu) · aarch64 190 MB — moderate

Ici, NDK 27.1.12297006 est conservé parce que c'est le NDK installé le plus récent (ou parce qu'un projet l'utilise), l'image système x86_64 ne peut pas démarrer sur Apple Silicon, et les caches de Gradle 8.13 sont exclus de la sélection intelligente tant qu'un démon tourne.

Un paquet du SDK ou une version de Gradle ne vaut la peine d'être supprimé que si aucun projet n'en a besoin. Pour le savoir, lu-cleaner lit vos projets : il parcourt vos racines de projets et vos racines de worktrees (voir Configuration) jusqu'à max_depth niveaux (8 par défaut), en sautant les dossiers volumineux comme node_modules, build et Pods, et lit les fichiers suivants :

Fichier Ce qu'il indique à lu-cleaner
gradle/wrapper/gradle-wrapper.properties la distribution Gradle du wrapper (distributionUrl)
build.gradle, build.gradle.kts, gradle.properties ndkVersion, buildToolsVersion, compileSdk, la version de CMake, org.gradle.java.home
app.json, app.config.js, app.config.ts les versions définies via expo-build-properties d'Expo
local.properties sdk.dir et ndk.dir
node_modules/react-native/gradle/libs.versions.toml le NDK, les build-tools et le compileSdk avec lesquels React Native compile

Quand un projet React Native n'a pas de node_modules, la version de react-native de son package.json donne le NDK par défaut de cette version. Les versions de Gradle installées avec Homebrew ou SDKMAN comptent aussi comme utilisées.

Ce parcours utilise toujours vos racines configurées (ou détectées automatiquement). Les racines passées en ligne de commande (--root, lu-cleaner artifacts <root>) ne restreignent que le scan des artefacts : avec un seul projet comme racine, toutes les versions utilisées par vos autres projets sembleraient inutilisées.

lu-cleaner cherche les SDK Android dans cet ordre, et les dédoublonne d'après leur chemin réel :

  1. $ANDROID_HOME, puis $ANDROID_SDK_ROOT
  2. les réglages d'Android Studio (options/android.sdk.path.xml de chaque dossier de réglages AndroidStudio*)
  3. sdk.dir dans les local.properties de vos projets, seulement si ce chemin existe (un fichier commité peut désigner le SDK d'une autre machine)
  4. l'emplacement par défaut ~/Library/Android/sdk
  5. le paquet android-commandlinetools de Homebrew (/opt/homebrew/share/… et /usr/local/share/…)

Les variables d'environnement sont lues d'abord dans l'environnement, puis dans vos fichiers de démarrage du shell (.zshenv, .zprofile, .zshrc, .bash_profile, .bashrc, .profile), car les applications graphiques et les agents IA n'héritent pas des exports de votre shell. Seules les affectations simples sont comprises : chemins littéraux, ~, $HOME et variables déjà définies ; tout ce qui est calculé avec $(...) ou des backticks est ignoré.

La même résolution s'applique au dossier des AVD ($ANDROID_AVD_HOME, $ANDROID_EMULATOR_HOME/avd, $ANDROID_USER_HOME/avd, $ANDROID_PREFS_ROOT/.android/avd, puis ~/.android/avd), à $GRADLE_USER_HOME (par défaut ~/.gradle) et à $JAVA_HOME.

Une configuration courante consiste à déplacer le SDK et les émulateurs sur un SSD externe en laissant des liens symboliques à leur place, par exemple ~/Library/Android/sdk qui pointe vers /Volumes/External/Android/sdk. lu-cleaner résout chaque lien symbolique et vérifie sur quel volume se trouvent les vrais fichiers. Tout ce qui ne se trouve pas sur le volume de votre dossier personnel est signalé, jamais supprimé, et les liens symboliques eux-mêmes ne sont jamais retirés :

Situation Ce que vous voyez
Fichiers sur un autre volume Android SDK on External, on external volume — no internal gain
Disque non monté on /Volumes/External, which is not mounted — nothing to clean here (link kept)
Cible du lien symbolique absente dangling symlink to … — nothing to clean here (link kept)
/Volumes/External est un simple dossier sur le disque interne … is a plain folder on the internal disk, not a mounted volume — check it by hand
SDK sur le disque interne mais hors de votre dossier personnel (Homebrew) mesuré, outside your home folder — remove it by hand if unused

Un dossier d'AVD dans la même situation apparaît comme Android emulators (3 AVDs) on External, avec les mêmes avertissements, et ses émulateurs sont listés à titre d'information uniquement.

Pourquoi : supprimer des fichiers sur un disque externe ne libère rien sur le disque interne que vous cherchez à nettoyer, et quand le disque n'est pas monté, lu-cleaner ne peut pas voir ce qu'il contient. Les deux derniers cas se trouvent en dehors des zones autorisées par le garde-fou de sécurité. À l'intérieur d'un SDK interne, un paquet isolé qui est un lien symbolique est signalé de la même façon.

Un AVD se compose d'un dossier <name>.avd et d'un fichier <name>.ini placé à côté. lu-cleaner affiche un élément par AVD, par exemple AVD Pixel 8 · API 35 · arm64-v8a, et supprime les deux.

AVD Risque Sélection intelligente
AVD ordinaire caution jamais : il contient des apps installées, leurs données et des snapshots
AVD x86 ou x86_64 sur Apple Silicon moderate oui : il ne peut pas démarrer sur ce Mac
Entrée cassée (un .ini dont le dossier a disparu) safe oui : seul le .ini est supprimé
Émulateur en cours d'exécution comme ci-dessus avertissement emulator is running — close it first

D'autres avertissements vous signalent un AVD sans .ini (l'émulateur ne le voit pas) ou dont l'image système n'est pas installée (il ne peut pas démarrer). Recréer un AVD dans le Device Manager d'Android Studio est rapide, mais ses apps et ses données sont perdues.

Le dossier snapshots/ d'un AVD contient default_boot, l'état de la RAM et de l'appareil enregistré à la fermeture pour Quick Boot. Le supprimer ne coûte que du temps : le lancement suivant se fait à froid (30 à 90 secondes), et les apps et les données restent. L'élément, Quick Boot snapshots · Pixel 8, est moderate et recommandé une fois périmé.

Si vous avez enregistré vous-même des snapshots nommés, l'élément devient Emulator snapshots · Pixel 8, avec le risque caution et un avertissement qui les liste. lu-cleaner ne supprime jamais les images userdata d'un AVD à elles seules : les snapshots en dépendent.

Les NDK, build-tools, plateformes, CMake et sources suivent les mêmes règles de conservation :

  • Utilisé par un projet : conservé. L'élément affiche used_by (dans le panneau de détails et dans les métadonnées --json) et n'est jamais recommandé.
  • Version installée la plus récente : toujours conservée. Réglez keep_latest dans la configuration pour conserver davantage de versions parmi les plus récentes (1 par défaut).
  • Tout le reste : recommandé, à condition que le parcours des projets soit allé jusqu'au bout.

Les images système suivent plutôt les émulateurs : une image utilisée par un AVD ne peut pas être sélectionnée, et une image inutilisée est recommandée.

Paquet Type (-k) Conservé quand Pour le récupérer
Images système, system-images/android-<API>/<tag>/<abi> android-system-image un AVD l'utilise (elle ne peut alors pas être sélectionnée du tout) SDK Manager, 1,5 à 7 Go chacune
NDK, ndk/<version> android-ndk un projet l'utilise, ou c'est le plus récent AGP le télécharge de nouveau (~1 Go)
Build-tools, build-tools/<version> android-build-tools un projet les utilise, ou ce sont les plus récents AGP les télécharge automatiquement
Plateformes, platforms/android-<N> android-platform c'est le compileSdk d'un projet, ou la plus récente AGP la télécharge automatiquement
CMake, cmake/<version> android-cmake un projet l'utilise, c'est le plus récent, ou c'est 3.22.1 (la version par défaut d'AGP 8, avec laquelle React Native compile) AGP télécharge la version dont il a besoin
Sources, sources/android-<N> android-sources c'est le compileSdk d'un projet, ou les plus récentes servent uniquement à parcourir le code dans l'IDE

Les images système sont moderate, comme les autres paquets. Si un dossier d'AVD ne peut pas être lu (disque non monté), les images portent an AVD folder is not reachable — cannot tell whether an emulator uses this image et ne sont pas recommandées. Un dossier nommé comme 35.0.0-2 est le reste d'une installation qui a échoué : safe et recommandé.

Contenu ancien et restes du SDK :

Élément Type (-k) Risque Sélection intelligente
Fichiers temporaires et téléchargements partiels du SDK Manager (.temp, .downloadIntermediates, temp) android-sdk-temp safe oui ; ignoré tant qu'Android Studio tourne
Anciens SDK Tools, tools/ android-sdk-legacy-tools moderate oui, quand cmdline-tools/latest est installé
Copies cmdline-tools/<version> remplacées android-cmdline-tools-old moderate oui ; latest est celle qui est utilisée
Ancien ndk-bundle android-ndk-bundle moderate oui, sauf si un projet fixe ndk.dir ou ndkPath
Intel HAXM (extras/intel) sur Apple Silicon android-haxm moderate oui

Les images système et AVD x86 et x86_64 ne peuvent pas démarrer sur un Mac Apple Silicon ; ce sont des restes typiques d'une migration depuis un Mac Intel. lu-cleaner ajoute cannot boot on Apple Silicon à leur note et les recommande. C'est une raison de les supprimer plutôt qu'un danger : ce n'est donc pas affiché comme un avertissement.

Gradle range tout dans son dossier utilisateur, $GRADLE_USER_HOME ou ~/.gradle. Si ce dossier se trouve sur un autre volume, il est signalé et jamais nettoyé.

Élément Type (-k) Risque Sélection intelligente et remarques
Distribution du wrapper, wrapper/dists/gradle-<v>-bin (ou -all) android-gradle-dist moderate recommandée quand aucun wrapper de projet ne l'utilise ; ./gradlew la retélécharge (130 à 200 Mo)
Téléchargement du wrapper interrompu (sans marqueur .ok) android-gradle-dist safe recommandé, sauf si un fichier .part a changé dans la dernière heure
Caches par version, caches/<version> android-gradle-version-cache moderate recommandés quand aucun wrapper ni aucun Gradle Homebrew ou SDKMAN n'utilise cette version
Transforms, jars ou build cache dans un ancien format (par exemple transforms-3 quand transforms-4 existe) android-gradle-transforms, android-gradle-jars, android-gradle-build-cache safe recommandés
Build cache local actuel android-gradle-build-cache safe sert uniquement à accélérer les rebuilds ; se remplit de nouveau tout seul
Cache de transforms actuel android-gradle-transforms moderate le dexing, jetifier et l'extraction des AAR sont recalculés (quelques minutes de plus)
Cache des dépendances, caches/modules-2 android-gradle-modules moderate 1 à 3 Go retéléchargés par le build suivant ; les builds hors ligne échouent d'ici là
Autres caches partagés (Kotlin DSL, scripts, keyrings…) android-gradle-caches-other moderate reconstruits ou retéléchargés
Journaux des démons, daemon/<version> android-gradle-daemon-logs safe tant qu'un démon de cette version tourne, seuls les journaux de plus de 24 heures sont proposés
Fichiers temporaires et téléchargements partiels, .tmp android-gradle-tmp safe fichiers de plus d'un jour, que ni Gradle ni les autres outils de nettoyage ne suppriment ; recommandés
JDK de toolchain, jdks/* android-gradle-jdk moderate retéléchargés (200 à 300 Mo) quand un build les demande ; jamais recommandés
Helpers natifs, rapports de build Kotlin, état des workers android-gradle-misc safe recréés automatiquement

caches/journal-1 n'est jamais proposé : le nettoyage de cache intégré à Gradle en dépend.

La date de dernière utilisation d'une version de Gradle provient de ses journaux de démon, le signal d'utilisation le plus fiable. Une distribution ou un cache qu'un projet utilise n'est pas recommandé comme inutilisé, mais comme tout élément moderate, il est recommandé une fois resté inutilisé plus longtemps que stale_after ; le build suivant de ce projet le retélécharge ou le reconstruit alors. Voir Niveaux de risque et sélection intelligente.

Les démons Gradle sont des processus JVM nommés java : lu-cleaner ne peut donc pas bloquer le nettoyage d'après le nom du processus, comme il le fait pour Xcode ou Android Studio. À la place, quand pgrep trouve un GradleDaemon, les éléments concernés reçoivent cet avertissement et sont exclus de la sélection intelligente :

Gradle daemon running — stop it first (./gradlew --stop or pkill -f GradleDaemon)

Les éléments propres à une version (distributions, caches par version) ne sont signalés que si un démon de cette version tourne, ou si la version d'un démon ne peut pas être lue. Les caches partagés (transforms, jars, build cache, modules-2, autres caches, JDK de toolchain, divers) sont signalés dès qu'un démon tourne.

Pourquoi c'est important : un démon en cours d'exécution garde les fichiers supprimés ouverts, donc leur espace n'est pas libéré avant qu'il se termine, et un build en cours peut échouer à mi-chemin. clean --yes retient ces éléments, sauf si vous passez --risk caution, mais l'avertissement n'arrête pas un nettoyage que vous sélectionnez vous-même dans le sélecteur : arrêtez donc les démons d'abord. ./gradlew --stop arrête les démons de la version de Gradle de ce projet ; pkill -f GradleDaemon les arrête tous.

Élément Type (-k) Risque Remarques
Caches de la version installée, ~/Library/Caches/Google/AndroidStudio<version> android-studio-cache moderate reconstruits au lancement suivant (la réindexation prend plusieurs minutes) ; ignorés tant qu'Android Studio tourne
Caches d'une version qui n'est plus installée android-studio-cache safe plus rien ne les lit ; recommandés
Journaux, ~/Library/Logs/Google/AndroidStudio* android-studio-logs safe idea.log et rapports de crash
Réglages d'une ancienne version, ~/Library/Application Support/Google/AndroidStudio* android-studio-config moderate lus uniquement pour migrer les réglages lors d'une mise à jour ; recommandés quand la version actuelle a ses propres réglages, jamais tant qu'une version installée n'a pas encore été lancée (elle les importe à son premier lancement)
Cache du dépôt du SDK, ancien build cache et dumps de crash de l'émulateur dans ~/.android android-user-cache safe uniquement le contenu de cache, build-cache et breakpad

Le dossier de réglages de la version que vous utilisez n'est jamais proposé. lu-cleaner détermine les versions installées à partir du product-info.json de chaque Android Studio*.app trouvée dans /Applications, ~/Applications et les résultats de Spotlight. S'il n'en trouve aucune (emplacement personnalisé, JetBrains Toolbox…), le dossier de réglages utilisé le plus récemment est considéré comme celui de la version actuelle, par prudence. Avec keep_latest au-dessus de 1, ce nombre de versions utilisées récemment comptent comme actuelles.

Un dossier de caches peut contenir LocalHistory, l'historique des modifications de vos fichiers tenu par l'IDE. Il n'est jamais proposé : l'élément vise alors les autres entrées du dossier.

Dans ~/.android, adbkey, debug.keystore et les réglages de l'émulateur ne sont jamais touchés : ils figurent sur la liste protégée du garde-fou.

Les builds Android ont besoin d'un JDK, et la plupart des machines en accumulent plusieurs. lu-cleaner les liste tous et n'en recommande jamais aucun :

Emplacement Installé par Traitement
/Library/Java/JavaVirtualMachines installeurs système, casks Homebrew rapport seul : la suppression demande des droits administrateur ; la note suggère brew uninstall --cask <cask> ou sudo rm -rf …
~/Library/Java/JavaVirtualMachines téléchargements depuis l'IDE moderate
~/.sdkman/candidates/java SDKMAN moderate
~/.local/share/mise/installs/java mise moderate
~/.asdf/installs/java asdf moderate

Un JDK référencé par $JAVA_HOME (environnement ou profil du shell) ou par org.gradle.java.home dans ~/.gradle/gradle.properties est marqué comme utilisé et ne peut pas être sélectionné. Les détails indiquent aussi si un JDK est enregistré dans la table des JDK d'Android Studio, s'il est antérieur au JDK 17 (React Native 0.73+ a besoin du 17), et avertissent des JDK x86_64 sur Apple Silicon (x86_64 JDK: runs only under Rosetta).

Ces emplacements fixes proviennent du catalogue :

Élément Emplacement Risque
État du démon Kotlin ~/.kotlin, ~/Library/Application Support/kotlin/daemon moderate
Dépendances et caches Kotlin/Native ~/.konan/dependencies, ~/.konan/cache moderate
Compilateurs Kotlin/Native (le plus récent est conservé) ~/.konan/kotlin-native-prebuilt-* moderate
Dépôt Maven local ~/.m2/repository caution : les artefacts que vous avez publiés localement ne peuvent pas être retéléchargés
Bibliothèques natives Skiko (la plus récente est conservée) ~/.skiko/* safe
Fichiers temporaires de l'émulateur $TMPDIR/android-* safe ; ignorés tant qu'un émulateur tourne
Appareils virtuels Genymotion ~/.Genymobile/Genymotion/deployed/* rapport seul : supprimez-les depuis Genymotion
APK stockés pour Maestro ~/.maestro/apps/android/*.apk rapport seul
  1. Arrêtez les démons Gradle, fermez tous les émulateurs et quittez Android Studio :

    Fenêtre de terminal
    pkill -f GradleDaemon
  2. Vérifiez que vos racines de projets couvrent tous vos projets Android, pour que la mention « inutilisé » soit fiable :

    Fenêtre de terminal
    lu-cleaner config show
  3. Passez tout en revue, y compris les éléments en rapport seul comme un SDK sur un disque externe :

    Fenêtre de terminal
    lu-cleaner scan -c android --all
  4. Ouvrez le sélecteur, appuyez sur a pour sélectionner les éléments recommandés (ou choisissez avec space), vérifiez-les catégorie par catégorie, puis appuyez sur d :

    Fenêtre de terminal
    lu-cleaner clean -c android
Fenêtre de terminal
# Le plan que la sélection intelligente exécuterait ; rien n'est supprimé
lu-cleaner clean --yes --smart -c android --dry-run
# Images système inutilisées (images x86 sur Apple Silicon comprises)
lu-cleaner clean --yes --smart -k android-system-image
# Distributions Gradle et caches par version dont aucun projet n'a besoin
lu-cleaner clean --yes --smart -k android-gradle-dist,android-gradle-version-cache --dry-run
# Fichiers temporaires de Gradle et journaux des démons
lu-cleaner clean --yes -k android-gradle-tmp,android-gradle-daemon-logs

Tous les types ci-dessus peuvent être passés à -k, et l'identifiant de fournisseur android sélectionne d'un coup tous les types du scanner Android. Les entrées du catalogue de la section précédente ont leurs propres identifiants (android-maven-local, android-konan-dependencies…), listés dans le catalogue. Les métadonnées de chaque élément (used_by, kept, api, abi, version, real_path…) sont disponibles dans lu-cleaner scan --json ; voir Sortie JSON.