com.google.android.gms:play-services-location:21.0.0にて、FusedLocationProviderClient
, ActivityRecognitionClient
, GeofencingClient
, SettingsClient
がclassからinterfaceに変わったらしいです。
FusedLocationProviderClient
,ActivityRecognitionClient
,GeofencingClient
andSettingsClient
are now interfaces instead of classes, which helps enforce correct usage and improves testability.
20.0.0 までのFusedLocationProviderClient
の使い方は以下のような形かと思います。
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も使えるようになったことで、以下のようによりシンプルにかけるように変わっています。
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になっているため、違うじゃん、ってエラーになってしまったわけです。
マルチモジュール環境では以下のような状況ですね。
dependencies {
implementation(project(":mylibrary"))
implementation("com.google.android.gms:play-services-location:21.0.1")
}
dependencies {
implementation("com.google.android.gms:play-services-location:20.0.0")
}
これはどうしようもないので、アプリの方はライブラリが対応するまで、20.0.0 を利用し、ライブラリの方で対応ができたら、それに併せて 21.0.1 に上げられるようになります。
ライブラリの方からplay-services-locationの依存関係をアプリの方にも伝搬させるようにしておけば、アプリ側でplay-services-locationのバージョン更新を忘れていても自動的に高い方にあわせることができます。
dependencies {
implementation(project(":mylibrary"))
implementation("com.google.android.gms:play-services-location:20.0.0")
}
dependencies {
implementation("com.google.android.gms:play-services-location:21.0.1")
}
ということで、play-services-location を内部で使っているライブラリをアプリに組み込むときは、バージョンの不一致に注意しようって話でした。