React Native 環境構築時に sdkmanager に NoClassDefFoundError って言われたという話

調べるのに結構かかったので、備忘録として。
なお、今回参照した公式ドキュメントの選択肢は下記の通りです。

- React Native CLI Quickstart
- macOS
- Android

## 記事の要約

- React Native の公式ドキュメント通りに環境構築したけどエラーが起きたよ
- `sdkmanager` を起動すると `NoClassDefFoundError`って言われたよ
- なぜか`sdkmanager`が2つあって、もう片方の `sdkmanager`はちゃんと動いたよ

## ことの始まり

React Native でアプリ開発してイケイケになりたいという気持ちで環境構築を始めました。
参考にしたのは下記の公式ドキュメントです。

https://reactnative.dev/docs/environment-setup

選択肢はそれぞれ下記の通りです。

- React Native CLI Quickstart
- macOS
- Android

ドキュメントに従い進めていきます。Android Studio は事前にインストールしてあったのでそちらを使います。
`2. Install the Android SDK`の最後に「`ANDROID_HOME`の設定が終われば `sdkmanager`のコマンドが叩けるよ(意訳)」と書いてあったので、`sdkmanager`を叩く前に `3. Configure the ANDROID_HOME environment variable`を進めました。
というわけで、設定を `~/.zshrc`に入れました。

```.zshrc
# 不要な部分は省略
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
```

環境変数を適用するためにターミナルを再起動してから `sdkmanager`を叩きました。

```console
$ sdkmanager
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
        at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
        at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
        at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 5 more
```

うん？

## 調べたこと

さて、なんとかしなければReact Nativeを使うことができません。
Google先生で調べてみましたが、出てくるのはだいたい下記のような情報です。

> 自前のJava11じゃなくてAndroid Studio同梱のJava1.8を使えばOK

ところが、私のAndroid StudioにはJava11が同梱されています。

```console
$ /Applications/Android\ Studio.app/Contents/jre/Contents/Home/bin/java --version
openjdk 11.0.11 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
OpenJDK 64-Bit Server VM (build 11.0.11+0-b60-7590822, mixed mode)
```

また、公式ドキュメントでもJava11を導入するように指示があります。

> __Java Development Kit__
> We recommend installing JDK using Homebrew. Run the following commands in a Terminal after installing Homebrew:
>>brew install --cask adoptopenjdk/openjdk/adoptopenjdk11

Java1.8を利用するのは想定されていないと考えるのが妥当でしょう。
とはいえ、そこから答えに辿り着くのが結構大変でした。

## 解決策

解決策が見つかったのは下記のページです。

https://www.reddit.com/r/androiddev/comments/snn2be/macos_sdkmanager_javalangnoclassdeffounderror/

> which sdkmanager -> this should point to /Users/brunnock/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager
> sdkmanager --version -> this should show 6.0

`sdkmanager`のパスとバージョンが指示されていますね。
バージョンについては`latest`(6.0)しかインストールしていないので大丈夫だと思います。
一方で、パスについては下記の通りに指定したはずです。

```.zshrc
# 不要な部分は省略
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin # ←ココ
export PATH=$PATH:$ANDROID_HOME/platform-tools
```

つまり、指定する`sdkmanager`のパスが誤っているように思えます。
そこで上記を修正して再度`sdkmanager`を叩いてみましょう。

```.zshrc
# 不要な部分は省略
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
# export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin # 追加
```

```console
$ sdkmanager
[=======================================] 100% Computing updates...  
```

やったぜ！
って誤っていた方のパス、公式ドキュメントの指定だったはずですが……うーん？

## エラーの原因

せっかくなのでもう少し確認してみましょう。
エラーの起きた方の`sdkmanager`と正常に動作した方の`sdkmanager`を比較してみます。

```sdkmanager:エラーが起きた方
#!/usr/bin/env sh

##############################################################################
##
##  sdkmanager start up script for UN*X
##
##############################################################################

# 以下省略
```

```sdkmanager:正常に動作した方
#!/usr/bin/env sh

##############################################################################
##
##  sdkmanager start up script for Mac
##
##############################################################################

# 以下省略
```

ここでは省略して一部分しか載せていませんが、何箇所か違いがありました。
差分を見る限り、正常に動作したのはMac用の `sdkmanager`のようです。
つまり、Mac用ではない `sdkmanager`を使っていたから想定通りにシェルスクリプトが動作せずにエラーが起きていたんですね。

なぜ指定のディレクトリにMac用ではない `sdkmanager`がいたのかは結局わからずじまいですが……
Android Studioは事前にインストールしてあったのでドキュメント通りの操作ではなく、もしかしたらそれが原因かなぁとも思いますが、確認はしていないです。

## 終わりに

というわけで、Macで `sdkmanager`に `NoClassDefFoundError`と言われたらパスの指定が誤っていることを疑ってみてもいいかも、という話でした。