FusedLocationProviderClientがclassからinterfaceになった。ライブラリとのバージョン不一致に気をつけろ

com.google.android.gms:play-services-location:21.0.0にて、`FusedLocationProviderClient`, `ActivityRecognitionClient`, `GeofencingClient`, `SettingsClient`がclassからinterfaceに変わったらしいです。

https://developers.google.com/android/guides/releases
> `FusedLocationProviderClient`, `ActivityRecognitionClient`, `GeofencingClient` and `SettingsClient` are now interfaces instead of classes, which helps enforce correct usage and improves testability.

20.0.0 までの`FusedLocationProviderClient`の使い方は以下のような形かと思います。

```kotlin
LocationServices.getFusedLocationProviderClient(this)
    .requestLocationUpdates(
        LocationRequest.create().setInterval(5000L),
        object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                // handle result
            }
        },
        Looper.getMainLooper()
    )
```

21.0.0 では、`LocationRequest.Builder`が追加されたことで、`LocationRequest.create()`がDeprecatedになっており、abstruct classであるLocationCallbackの代わりに、interfaceであるLocationListenerも使えるようになったことで、以下のようによりシンプルにかけるように変わっています。

```kotlin
LocationServices.getFusedLocationProviderClient(context)
    .requestLocationUpdates(
        LocationRequest.Builder(5000L).build(), {
            // handle result
        },
        Looper.getMainLooper()
    )
```

とはいえ、旧来の記述のままでも、implementationのバージョンを書き換えるだけで特に問題無く動作します。

ただし、これをライブラリで使っている場合に問題が発生するということを経験しました。

# java.lang.IncompatibleClassChangeError

java.lang.IncompatibleClassChangeErrorが発生しました。メッセージによると`FusedLocationProviderClient`はクラスだと思ってたのにインターフェースだったってことです。

```
FATAL EXCEPTION: main
Process: net.mm2d.myapplication, PID: 4946
java.lang.IncompatibleClassChangeError: Found interface com.google.android.gms.location.FusedLocationProviderClient, but class was expected (declaration of 'com.google.android.gms.location.FusedLocationProviderClient' appears in /data/app/~~6HpKRmhmmxafqGImwiJmvg==/net.mm2d.myapplication-szASdrDCRTpbZKYoLEPVbw==/base.apk)
	at net.mm2d.mylibrary.Locations.get(Locations.kt:28)
	at net.mm2d.myapplication.MainActivity.onCreate$lambda$1(MainActivity.kt:46)
	at net.mm2d.myapplication.MainActivity.$r8$lambda$YO0xJVQBy7fTOQisoSQWMfyKSqw(Unknown Source:0)
	at net.mm2d.myapplication.MainActivity$$ExternalSyntheticLambda1.onClick(Unknown Source:2)
	at android.view.View.performClick(View.java:7506)
	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219)
	at android.view.View.performClickInternal(View.java:7483)
	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
	at android.view.View$PerformClick.run(View.java:29335)
	at android.os.Handler.handleCallback(Handler.java:942)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.app.ActivityThread.main(ActivityThread.java:7898)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
```

原因は、ライブラリが20.0.0を使っており、アプリの方で21.0.1を組み込んだからです。ライブラリは`FusedLocationProviderClient`がclassである環境でコンパイルされているが、アプリの方で21.0.1を指定したので、依存関係からより新しい、21.0.1 が組み込まれます。 21.0.1 では`FusedLocationProviderClient`がinterfaceになっているため、違うじゃん、ってエラーになってしまったわけです。

マルチモジュール環境では以下のような状況ですね。

```gradle:app/build.gradle.kts
dependencies {
    implementation(project(":mylibrary"))
    implementation("com.google.android.gms:play-services-location:21.0.1")
}
```

```gradle:mylibrary/build.gradle.kts
dependencies {
    implementation("com.google.android.gms:play-services-location:20.0.0")
}
```

これはどうしようもないので、アプリの方はライブラリが対応するまで、20.0.0 を利用し、ライブラリの方で対応ができたら、それに併せて 21.0.1 に上げられるようになります。
ライブラリの方からplay-services-locationの依存関係をアプリの方にも伝搬させるようにしておけば、アプリ側でplay-services-locationのバージョン更新を忘れていても自動的に高い方にあわせることができます。

```gradle:app/build.gradle.kts
dependencies {
    implementation(project(":mylibrary"))
    implementation("com.google.android.gms:play-services-location:20.0.0")
}
```

```gradle:mylibrary/build.gradle.kts
dependencies {
    implementation("com.google.android.gms:play-services-location:21.0.1")
}
```

ということで、play-services-location を内部で使っているライブラリをアプリに組み込むときは、バージョンの不一致に注意しようって話でした。