minne事業部プロダクト開発チームのtepiです。今回はadbを使ってAndroidアプリに実装したServiceを実行する方法をご紹介したいと思います。 特に、デバッグ用アプリでパッケージ名が違う場合等に、どこに何を指定すれば良いか具体的に知りたい方の助けになれば幸いです。
前提
adb
コマンドを実行できるようにしておく必要があります。
Serviceの実装は通常と変わらず、実装後にAndroidManifestに記載してください。
また、adbの仕様に関しては、公式ドキュメントをご確認ください。
呼び出し方
以下をコマンドライン上で実行することで端末上のアプリのServiceを実行することができます。
adb shell am startservice -n <アプリのパッケージ名>/<Serviceクラスのパッケージ名>.<Serviceクラス名>
例えば、
- アプリのパッケージ名が
jp.example.sample.dev
- Serviceクラスのパッケージ名が
jp.example.sample.feature.service
- クラス名が
TestService
の場合、以下の通りとなります。
adb shell am startservice -n jp.example.sample.dev/jp.example.sample.feature.service.TestService
nオプションはアプリのパッケージやクラスを指定するために利用するオプションです。-nの部分は無くても動作しますが、明示できるようつけています。
サービスに引数を渡したい場合
以下のようにe
オプションにkey、valueを指定することでServiceに文字列の値を渡す事ができます。
adb shell am startservice -n <アプリのパッケージ名>/<Serviceクラスのパッケージ名>.<Serviceクラス名> -e key value
複数の場合は、e
オプションを更に増やすことで追加できます。
adb shell am startservice -n <アプリのパッケージ名>/<Serviceクラスのパッケージ名>.<Serviceクラス名> -e key value -e key2 value2
なお、文字列以外を渡したい場合は、--ei
はInt、--ez
はBooleanと各種用意されています。詳しくはドキュメントのSpecification for intent argumentsの項目をご確認ください。
Serviceの実装では以下のようにすることで、Bundle
で渡された引数を受け取る事ができます。
val value = if (intent?.hasExtra("key") == true) {
intent.getStringExtra("key") ?: ""
} else {
""
}
val value2 = if (intent?.hasExtra("key2") == true) {
intent.getIntExtra("key2", 0)
} else {
0
}
-e
で指定した値は、getStringExtra()
で、--ei
で指定した値はgetIntExtra()
で取得できます。
なお、-e
で数字を指定した場合(例:-e number 1
)は、getStringExtra()
で受け取ることになり、結果は数字の文字列になります。
Foreground Serviceの実行
ServiceをForegroundで実行しないといけない場合は、以下のようにstartservice
をstart-foreground-service
に変えることで実行できます。
adb shell am start-foreground-service -n <アプリのパッケージ名>/<Serviceクラスのパッケージ名>.<Serviceクラス名>
もしくは、アプリのプロセスがAndroid Studioのデバッグで認識できる状態(例:アプリを起動する、起動後バックグラウンドにいる状態にする等)であれば、startservice
のままでも実行できます。
Serviceの終了
Serviceが永続的に実行されない場合は上記のコマンドのみでも終了しますが、永続的に実行されるServiceの場合は以下のようにして止めることができるようです。
adb shell am stopservice -n <アプリのパッケージ名>/<Serviceクラスのパッケージ名>.<Serviceクラス名>
もしくは
adb shell am force-stop <アプリのパッケージ名>
※minneでは永続的なServiceが無いため、上記は動作未確認となります。
ユースケース
minneでは、プッシュ通知のローカル端末上でのシミュレーションに利用しています。
リリース用アプリでは、プッシュ通知を受け取るServiceを実装する際に、実際にNotificationManagerCompatなどを利用してプッシュ通知を表示する実装をServiceと分離させました。また、デバッグ用アプリではプッシュ通知を受け取るServiceのMockのようなものを作って、当該のプッシュ通知を表示する実装とつなぎこんでいます。
そうすることで、実際にサーバー経由でプッシュ通知を行わずとも、サーバーから来るであろうプッシュ通知を模した値をServiceのMock経由でテストでき、実際の動作の確認がしやすくなりました。 また、ServiceのMockをデバッグ用アプリのビルドにだけ反映されるよう整理することで、本番アプリに影響を及ぼさない形でいつでも利用できる状態を維持しやすくなりましたし、気軽にテスト用のプッシュ通知の一覧をソース内に置いておくことができるようになりました。
まとめ
実際に利用しようとしていた際は調べるのに少し手間取ったのですが、記事を書く上で改めてadbのドキュメントを見てみると、Intentの起動に関しては記載がありもっと早く見ていれば簡単に答えにたどり着けたような気もします。また、反対にドキュメントにはないコマンド(例えば上記のstopserviceもそうですが)をWeb上で見かけることもあり、adbは奥深くまだまだ勉強が必要だなと感じました。
今回はベストプラクティスと言えるかは全くわかりませんが、今後開発していく上で役に立ちそうな部分を作れたかなと考えています。そういった未来に向けても引き続き対応をしていきたいですし、一緒により良い方法を目指していける方をぜひぜひお待ちしております!