hiro99ma blog

何か技術的なこと

android: compose で system service はどこでもらうか (2)

2024/10/23

あらすじ

BLE スキャンするために getSystemService()をどこで呼ぶと良いか悩む私。
呼ぶにしても Context がいるため、その面倒さが何にしてもネックになることに気付く。
検討していると DI(依存性注入)ライブラリを使うとパラメータによる直接の引渡しをしないで済みそうな気配を感じる。

前回の最後は Hilt という Android 向けの DIライブラリの使い方を調べていた。

@Composable invocations can only happen from the context of a @Composable function

パラメータを Int から Context にするとエラーになった。

image

“invocation” は「祈り」とか「実施」のような意味。call とは違うのか。
私は「実行する」(execute)か「呼び出す」(call)しか思いつかんな。

そういうのはともかく、コンテキストが違うと呼び出せないそうだ。
ここのラムダ式はコールバックみたいなものだと思っていたがコンテキストが違うのか。。。

私の場合もこちらと同じように composable関数の方でLocalContext.currentを保持してラムダ式の中で渡すことでエラーが出なくなった。

下の方ではこのContextを保持するわけではなく BluetoothManagerのインスタンスを取得するのに使う。
BluetoothManager –> BluetoothAdapter –> BluetoothLeScanner という流れだ。
下の方で保持するのだが Activity の作り直しが発生すると Context も変更になるのだろうか?

デバッガで確認したところ、composable 関数での Localcontext.currentMainActivity だった。
(ActivityApplicationandroid.appパッケージ。)

ではApplicationContextはどこから取得したら良いだろうか。 しばし悩んだが、Activity.getApplicationContext() で取得できるので同じように LocalContext.current.applicationContext でよい。

DI の便利さ

昨日 Hilt で DI した(という使い方で良いのか?)のは Context だった。
が、これは単に ScanViewModel のコンストラクタ引数を Context と書いて、呼び出す ScanScreen からも Activity の context を引数に書いただけで、全然 Hilt を導入したよさがない。
そもそもどうなるのが便利なんだっけ?

1つしか ViewModel がないとありがたみがわからないようだ。
複数 ViewModel があって、それぞれがまた Repository やらなんやらを使うようなところを参照するようなところが便利になるらしい。

それ以外にも、よく使うようなパラメータについては組み込み済みのアノテーションがあって楽して使えるようだ。
そう、今回は Context を上から直接もらうように書かなくても何とかなるんじゃないの、というのが目的だったのだ。

Context をどこかからもらう

というわけで、苦労した Hilt で ViewModel をパラメータ付きにする箇所を、パラメータをもらわないで済むように変更する。

(“ApplicationContext” というユーザがいたので、アノテーション @ApplicationContext を git コメントに書いたらその人へのリンクになったじゃないか。。。)

ViewModel の定義はこうなった。

@HiltViewModel
class ScanViewModel @Inject constructor(
    @ApplicationContext context: Context
): ViewModel() {

呼び出したのは ScanScreen 側。

これだけである。
ブレークポイントで止めると contextMainApplication になっていたので指示通りだ。
ちゃんとスキャンも動作したので Context に問題はないだろう。

ScanViewModel のコンストラクタ引数に 1つ Int を追加したところ、前の assisted injection に対応せずパラメータを追加したときと同じエラーが出た。

Context は全然出てこなかった。 そういうものなんだ。

DI再履修

なんか、これくらいだったらコンストラクタに Context を渡しても大して差がないような気がしてきた。
パラメータが増えてきたら面倒かもしれないが、たぶんそういう目的じゃないな。

DI の話と、DI ライブラリの話を混ぜ込んで考えてしまったようだ。
コンストラクタで「これ使ってね」と依存しているインスタンスを投げ込むのも DI なのだった。
あとは、そういうしくみが手動なのか自動なのかという話なのだな。

もうちょっと学習が必要なので、今日はここまで。
外部からインスタンスを受け入れるようにすれば DI というわけでもなさそうだ。

< Top page