Gradle ビルドキャッシュや KSP キャッシュを消すとき Gradle デーモン止めないと意味がない件

Android 開発をしていると、Git 操作後や依存関係を変更したあとに KSP(Kotlin Symbol Processing)やビルドキャッシュの問題 に遭遇することがあります。


java.lang.IllegalStateException: Storage for [C:\...\symbolLookups\id-to-file.tab] is already registered

この記事では、Gradle デーモンとキャッシュの関係を整理し、「キャッシュを消したのにビルドが直らない」問題 を回避する方法を解説します。

 

🤔 Gradle デーモンとは何か

Gradle はビルドの高速化のために デーモン と呼ばれるバックグラウンドプロセスを使います。

常駐プロセスとして、次回のビルドでキャッシュを再利用

複数プロジェクトやターミナルで同時に動作可能

Android Studio 再起動だけでは停止しない場合がある

デーモン状態の確認方法


./gradlew --status


No Gradle daemons are running

→ デーモン未起動


PID STATUS INFO
3494 IDLE 8.11.1

→ デーモン起動済み、待機中


PID STATUS INFO
3494 BUSY 8.11.1

デーモン停止は以下で可能:


./gradlew --stop

 

🤔 キャッシュを消しても直らない理由

KSP や Gradle のキャッシュを削除しただけでは、既に動作中のデーモンが古いキャッシュ情報を保持している場合があります。

典型的なケース


1. git rebase でコミットを整理

2. KSP が参照する Room の型情報や生成コードが変更

3. Gradle デーモンが古いキャッシュを保持 → ビルド失敗

つまり、キャッシュを消しても、デーモンが古い情報を持っている限り意味がありません。

 

🤔 解決手順まとめ

① Gradle デーモンを止める


./gradlew --stop

すべての Gradle デーモンを終了

次回ビルド時に新しいデーモンが起動

② KSP キャッシュを削除する


rm -rf .ksp/ */build/kspCaches */build/generated/ksp/

KSP が持つ生成キャッシュを完全に削除

再ビルド時に最新のコードから生成される

③ Android Studio のキャッシュをクリア


File → Invalidate Caches / Restart → Invalidate and Restart

IDE 側のキャッシュもクリア

デーモン再起動時に正しい情報を読み込む

④ KSP のインクリメンタルコンパイルを無効化(必要に応じて)

gradle.properties に追加:


ksp.incremental=false

キャッシュ競合によるエラー(例:java.lang.IllegalStateException: Storage for [C:\...\symbolLookups\id-to-file.tab] is already registered)を回避

 

🤔 Git Rebase 後の KSP ビルドフロー


Git Rebase
    ↓
ソースコード変更
    ↓
KSP キャッシュ古いまま
    ↓ (Gradle デーモン生きている場合)
ビルド失敗 → Storage already registered など
    ↓
./gradlew --stop でデーモン停止
    ↓
KSP キャッシュ削除
    ↓
再ビルド
    ↓
成功

 

🤔 まとめ

Gradle デーモンは Android Studio 再起動だけでは必ずしも止まらない

KSP やビルドキャッシュを消すときは必ずデーモンを止める

典型的な手順:


./gradlew --stop

rm -rf .ksp/ */build/kspCaches */build/generated/ksp/

Android Studio キャッシュクリア

ビルド再実行

これで Git 操作後や依存関係変更後のビルド失敗を防げる

Gradle デーモンの挙動を理解しておくと、「キャッシュ消したのに直らない」問題 に悩まされずに済みます。

ビルド高速化の恩恵を受けつつ、必要なときにはデーモンを適切に制御しましょう。

android - java.lang.IllegalStateException: Storage for [C:\...\symbolLookups\id-to-file.tab] is already registered - Stack Overflow


【macOS】「Toggle Sticky Selection」を設定したい!

JetBrains系IDE(Android Studio、IntelliJ IDEA、PyCharm、WebStorm など)では、「Sticky Selection(固定選択)」という便利なモードがあります。これは、カーソルを移動するだけでテキスト選択を継続できるモードで、範囲選択を効率化できます。

通常は「Toggle Sticky Selection」にショートカットを割り当てて使いますが、デフォルトでは設定されていないため、手動でキーを割り当てる必要があります。たとえば、筆者は Control + Space に割り当てようとしました。しかし、ここで問題が発生しました。

 

🤔 Control + Space を設定しようとすると、IMEが切り替わってしまう

ショートカット設定画面で「Toggle Sticky Selection」に Control + Space を登録しようとすると、なぜか入力欄が反応せず、MacのIME(日本語入力)が英語に切り替わってしまいます。

つまり、IDEのエディタがそのキー入力をキャッチする前に、macOSのシステムが先に反応してしまっているのです。

これは macOS のデフォルト設定で、Control + Space は「入力ソースの切り替え」に使われているからです。Macで複数の言語(たとえば日本語と英語)を切り替えている人にはおなじみのショートカットですね。

このままでは、IDEエディタ側でショートカット登録ができません。そこで、macOS側の設定を変更する必要があります。

 

🤔 macOSのショートカット設定を変更して回避する

macOS のシステム環境設定から、ショートカットの競合を解消しましょう。


1.「システム設定」アプリを開く

2. サイドバーから 「キーボード」 を選択

3. 右側にある 「キーボードショートカット…」 をクリック

4. サイドメニューから 「入力ソース」 を選択

5. 「前の入力ソースを選択」 に割り当てられている ^ スペース(Control + Space) をクリック

表示されたチェックを外す、もしくは他のショートカットに変更

これで macOS 側が Control + Space を使わなくなるため、IDE側の設定画面で正しくこのキーを登録できるようになります。

 

🤔 無事にショートカット登録が可能に!あとは設定するだけ

macOS 側の干渉がなくなれば、IDE側の設定画面で Control + Space を「Toggle Sticky Selection」に自由に割り当てられるようになります。


1. IDE の「設定(Preferences)」を開く

2. Keymap(キーマップ) を選択

3. 検索バーに「Sticky Selection」と入力

4. 「Toggle Sticky Selection」を右クリック → Add Keyboard Shortcut

5. Control + Space を入力 → OK

設定後、エディタ上で Control + Space を押すと Sticky Selection モードに入り、矢印キーなどでカーソルを動かすたびに選択範囲が伸びていくことを確認できます。

 

🧑🏻‍💻 まとめ:macOSの干渉を避ければ自由にカスタムできる

Control + Space は便利なショートカットですが、macOS のデフォルト設定でIME切り替えに使われているため、IDEのエディタではそのままでは登録できません。

しかし、macOS の「キーボードショートカット」設定から「入力ソースの切り替え」を無効化または変更すれば、競合を回避できます。

この一手間を加えることで、自分の作業スタイルにあったショートカット環境が整います。エディタをもっと快適に使うためにも、こうした細かい調整はぜひ試してみてください 🎉


Android Studio エディタのカスタムショートカット私の場合

 

🧑🏻‍💻 Emacs風キーバインドで快適操作:⌃キーの力

多くの開発者やパワーユーザーにとって、キーボードだけでカーソルを自在に動かせる操作感は非常に重要です。macOSの多くのアプリ(特にテキストエディタやIDE)では、Emacs由来のショートカットがそのまま使えます。以下はその代表的なキー操作です:


⌃ + P:1行上に移動(Previous)

⌃ + N:1行下に移動(Next)

⌃ + F:1文字前に移動(Forward)

⌃ + B:1文字後に移動(Backward)

⌃ + A:行の先頭に移動(Ahead)

⌃ + E:行の末尾に移動(End)

これらを習得すれば、矢印キーに手を伸ばす必要がなくなり、ホームポジションから手を離さずにテキスト編集が可能になります。エディタによってはこの操作がデフォルトで有効になっていない場合もありますが、設定から有効化できます。

 

🧑🏻‍💻 Emacs風の編集:削除と選択もショートカットで完結

カーソル移動だけでなく、削除や選択もEmacsライクなショートカットで完結できます。


⌃ + H:カーソルの左側を削除(Backspaceの代替)

⌃ + D:カーソルの右側を削除(Deleteの代替)

⌃ + Space:選択開始(Toggle Sticky Selection)

Escape:選択解除(選択をリセット)

これらのショートカットを組み合わせることで、マウスやトラックパッドを使わずにスムーズに範囲選択・編集が可能になります。特にコードを書く際や長文を扱う場面では、こうしたキー操作が生産性を大きく向上させます。

 

🧑🏻‍💻 Emacsタブ機能の活用

Emacsといえば「タブ機能」も有名です。エディタやIDEでも似たような概念が使われており、特定のコードブロックを補完したり、自動インデントする際に「Tab」キーを活用します。多くの環境では以下のような挙動になります:


インデント揃え:コードの構造を整える

スニペット展開:よく使うコードを自動入力

オートコンプリート:候補を自動で補完

設定によっては、Tabキーの動作を「Emacs風に統一」することもできます。たとえば、Tabで選択中の行をインデントする代わりに、関数の補完を優先させるようなカスタマイズが可能です。

 

🧑🏻‍💻 設定のエクスポートと移行:.xml形式で管理

キーバインドやタブ設定など、細かくカスタマイズした設定を他の環境にも移したい場合は、「設定のエクスポート/インポート」機能を活用しましょう。

最近の多くのエディタやIDEでは、設定ファイルが.xml形式で保存されるようになっています。このため、以下のような運用が可能になります:


エクスポート:現在の設定を .xml ファイルとして保存

インポート:他の環境にその .xml ファイルを読み込んで設定を反映

JetBrains系のIDEやVS Code、Emacs本体でも、設定のエクスポート・共有がサポートされており、作業環境を素早く再構築できます。リモートワークやマシン変更時にも便利です。

 

🧑🏻‍💻 まとめ:ホームポジションを守る操作が生産性を支える

今回紹介したEmacs風キーバインドとその活用法は、キーボード中心の作業スタイルを大きく支えてくれる技術です。特に、ホームポジションを崩さずにカーソル移動・テキスト編集・範囲選択・削除といった操作を完結できるのは、集中力の持続にも大きく寄与します。

また、設定をXMLでエクスポート/インポートできることで、作業環境をどこでも再現できるのも大きな利点です。特に複数のMacや開発環境を行き来する方には重宝するでしょう。

Emacsを深く使いこなす必要はありませんが、macOSで使えるこれらの基本キーバインドを身につけるだけでも、日々の操作が格段に快適になります。まずは1つずつ、手に馴染ませてみてください。

👉 【macOS】IDE で 矢印 (カーソルキー) を使うと キーボード ホームポジション がずれる件
👉 【macOS】キーマップとキーボードショートカットの変更設定
👉 【macOS】「⌫ (delete)」 と「⌦ (forward delete)」の キーボードショートカット設定
👉 ⌘英かな から Karabiner-Elements へ ⌨️


なぜ Android Studio のコピーは使いづらいのか? - 「常にプレーンテキストでコピーする」設定方法

Android Studio でテキストをコピーすると、コードの色やフォントなどの装飾(リッチテキスト)が一緒についてきます。

一見便利に見えますが、Slack やドキュメントに貼り付けたとき、意図せずスタイルが保持されてしまい、かえって見づらくなります。

特に、コードをブログやチャットに共有する場合、「ただのテキストとして貼りたいだけなのに……」という不満が出やすいです。

この問題は、標準のコピー操作(Cmd+C / Ctrl+C)がリッチテキスト形式を優先していることに起因しています。

 

🤔「常にプレーンテキストでコピーする」設定方法

この挙動は設定から変更可能です。Android Studio の Preferences(または Settings)を開いて、次の手順で設定を変えましょう:

1. メニューから Editor → General → Smart Keys に移動

2.「Copy as rich text」のチェックを外す

これで、今後 Cmd+C / Ctrl+C を押した際にも、余計な装飾を含まないプレーンテキストとしてコピーされるようになります。

特別なショートカットを使わなくても、普段通りの操作でコードだけをきれいにコピーできるので、ストレスが激減します。

 

🤔 コード共有の効率を上げるために

この設定変更は、日常的にコードを共有する開発者にとって大きなメリットになります。

Markdown に貼るとき、Google ドキュメントや Notion に記録するとき、いちいち「Copy as Plain Text」を選ぶ必要がなくなるため、作業効率が向上します。

また、受け取った側も余計なスタイルに惑わされず、コードそのものを読みやすくなります。

Android Studio はデフォルトで「見た目のきれいさ」を重視しますが、実際の開発現場では「シンプルで確実な共有」が求められる場面も多いです。

このような小さな設定変更が、日々の開発体験を大きく快適にしてくれます。

 

🤔 まとめ:小さな設定で大きな快適さを

Android Studio のコピー動作は、初期設定のままだと意図しないリッチテキスト形式でのコピーとなり、共有時に不便を感じがちです。

しかし、「Copy as rich text」のチェックを外すだけで、常にプレーンテキストとしてコピーできるようになり、

コード共有が格段にスムーズになります。

日々のちょっとしたストレスを減らし、開発に集中するためにも、ぜひ一度この設定を見直してみてください。


【curl】 Note: Unnecessary use of -X or --request, POST is already inferred.


❯ curl -X POST "https://httpbin.org/post" -H "accept: application/json"


Note: Unnecessary use of -X or --request, POST is already inferred.

-d や --data があると自動で POST と判断するので -X POST などは不要だって。


❯ echo '{
  "user_id" : 123,
  "name" : "ワロ田",
  "age" : 14
}' | curl -vs -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json' -d @- https://httpbin.org/post
* Host httpbin.org:443 was resolved.
* IPv6: (none)
* IPv4: 54.152.142.77, 3.224.7.64, 35.172.19.140, 34.238.6.191
*   Trying 54.152.142.77:443...
* Connected to httpbin.org (54.152.142.77) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=httpbin.org
*  start date: Aug 20 00:00:00 2024 GMT
*  expire date: Sep 17 23:59:59 2025 GMT
*  subjectAltName: host "httpbin.org" matched cert's "httpbin.org"
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://httpbin.org/post
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: httpbin.org]
* [HTTP/2] [1] [:path: /post]
* [HTTP/2] [1] [user-agent: curl/8.7.1]
* [HTTP/2] [1] [content-type: application/json; charset=UTF-8]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [content-length: 55]
> POST /post HTTP/2
> Host: httpbin.org
> User-Agent: curl/8.7.1
> Content-Type: application/json; charset=UTF-8
> Accept: application/json
> Content-Length: 55
>
* upload completely sent off: 55 bytes
< HTTP/2 200
< date: Sun, 13 Apr 2025 03:08:32 GMT
< content-type: application/json
< content-length: 557
< server: gunicorn/19.9.0
< access-control-allow-origin: *
< access-control-allow-credentials: true
<
{
  "args": {},
  "data": "{  \"user_id\" : 123,  \"name\" : \"\u30ef\u30ed\u7530\",  \"age\" : 14}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "application/json",
    "Content-Length": "55",
    "Content-Type": "application/json; charset=UTF-8",
    "Host": "httpbin.org",
    "User-Agent": "curl/8.7.1",
    "X-Amzn-Trace-Id": "Root=1-67fb2ab0-3685bdfa4519f20b0d94c818"
  },
  "json": {
    "age": 14,
    "name": "\u30ef\u30ed\u7530",
    "user_id": 123
  },
  "origin": "114.123.123.8",
  "url": "https://httpbin.org/post"
}
* Connection #0 to host httpbin.org left intact


❯ echo '{
  "user_id" : 123,
  "name" : "ワロ田",
  "age" : 14
}' | curl -s -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json' -d @- https://httpbin.org/post | jq
{
  "args": {},
  "data": "{  \"user_id\" : 123,  \"name\" : \"ワロ田\",  \"age\" : 14}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "application/json",
    "Content-Length": "55",
    "Content-Type": "application/json; charset=UTF-8",
    "Host": "httpbin.org",
    "User-Agent": "curl/8.7.1",
    "X-Amzn-Trace-Id": "Root=1-67fb2b2f-6faa217ece89a13acc5315"
  },
  "json": {
    "age": 14,
    "name": "ワロ田",
    "user_id": 123
  },
  "origin": "114.123.123.81",
  "url": "https://httpbin.org/post"
}

Form からの POST があるので

リクエストヘッダは2つつけたほうが

「JSON の POST ですよ」

とはっきり明示できそうです。

 

🧑🏻‍💻 参考