0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コルーチン・フローを使ったAndroidサービスとの通信

Posted at

RemoteSharedFlow クラスモジュールの紹介

参考記事:Kotlin Coroutine Flows and Android Services

GitHub:ソースコード(ライブラリーとサンプル)

クライアントとサービス間で送信されるデータは文字列

RemoteSharedFlowでは、クライアントとサービス間で送信されるデータとして文字列のみを使用するように設計されています。

これが選択された理由は、文字列を使用することの柔軟性です。

  • クライアントとサービスはデータクラスを共有する必要がなく、データクラスの定義のみを共有すればよい
  • 複雑なデータクラスであってもJSON文字列としてシリアル化・逆シリアル化できる
    → JSONは柔軟性があり、要素やフィールドが欠落している場合でもクラスを逆シリアル化できる
  • バイナリデータでさえもBase64を使用して文字列に変換することで転送できる
  • データクラス内のフィールドの順序を含む厳密なデータクラス定義を課すParcelableを使用する必要がない

使い方

リポジトリには、サンプルプログラム(app)が含まれています。

サービス

MainServiceクラスは、RemoteSharedFlowによって、バインドされるサービスです。
onCreate()関数で、RemoteSharedFlowのインスタンスを引数無しで取得し、onBind()関数で、バインダー(asBinder)を返しています。

MainService.kt
class MainService: Service() {

    private val tag = "Main Service"

    private lateinit var remoteSharedFlow: RemoteSharedFlow
    private val coroutineScope = CoroutineScope(Dispatchers.Default)

    override fun onCreate() {
        super.onCreate()
        remoteSharedFlow = remoteSharedFlow()
        coroutineScope.launch {
            remoteSharedFlow.flow().collect {
                println("$tag $it")
                remoteSharedFlow.emit("$tag Received: $it")
            }
        }
    }

    override fun onBind(intent: Intent?): IBinder {
        return remoteSharedFlow.asBinder()
    }

    override fun onDestroy() {
        super.onDestroy()
        coroutineScope.cancel()  // 本記事にて追加
    }
}

サービスとして、onCreate()関数のcoroutineScope.launchの処理(Job)で、文字列データを受け取り、それを加工して送信(返信)しています。

【処理の中断】
この処理(Job)は、永久的に繰り返されますので、onDestroy()関数内のcoroutineScope.cancel()で、処理(Job)を中断するようにソースコードを追加しています(オリジナルのサンプルでは実装されていません)。

クライアント

クライアントであるMainActivityアクテビティで、RemoteSharedFlowを用いて、MainServiceサービスを起動し、サービスと通信を行っています。

クライアント側では、サービスを起動(バインド)するために、remoteSharedFlow()関数の引数をしていします。

  • 第1引数: コンテキスト - MainActivity自身(this)
  • 第2引数: パッケージ名 - "com.example.remoteflow"
  • 第3引数: サービス名 - "com.example.remoteflow.MainService"
MainActivity.kt
class MainActivity : ComponentActivity() {

    private val tag = "Main Activity"

    private val coroutineScope = CoroutineScope(Dispatchers.Default)
    private lateinit var remoteSharedFlow: RemoteSharedFlow

    :
    
    override fun onStart() {
        super.onStart()
        remoteSharedFlow = remoteSharedFlow(
            this,
            "com.example.remoteflow",
            "com.example.remoteflow.MainService"
        )

        coroutineScope.launch {
            remoteSharedFlow.flow().collect {
                println("$tag Response1: $it")
            }
        }

        coroutineScope.launch {
            val counter = AtomicInteger(0)
            remoteSharedFlow.flow().collect {
                println("$tag Response2: $it")
                if (counter.incrementAndGet() == 10)
                    this.cancel()
            }
        }

        coroutineScope.launch {
            for (i in 1..100) {
                println("$tag sent: Hello there")
                remoteSharedFlow.emit("$tag Hello there")
                delay(2000)
            }
        }
    }
}

onStart()関数内で、3つの処理(Job1, Job2, Job3)を起動しています。

1つ目の処理(Job1)
サービスから受け取った文字列データを加工して、コンソール出力しています。

2つ目の処理(Job2)
1つ目の処理(Job1)と同様に、サービスから受け取った文字列データを加工して、コンソール出力していますが、10回目で、処理(Job2)を中断しています。

3つ目の処理(Job2)
2秒毎に100回、文字列データをサービスに送信しています。

サービスの停止(バインド解除)

オリジナルのRemoteSharedFlowクラスでは、サービスを起動(バインド)することはできますが、停止(バインド解除)することができません。RemoteSharedFlowクラスを改良して、serviceConnectionとのバインドを解除する手段を追加すると良いのかもしれません。

バインドしている箇所 (RemoteSharedFlow_Impl.kt#L85-L87)
    if (!it.bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE)) {
        it.unbindService(serviceConnection)
        throw BindException()
    }
バインドを解除する手段
    context.unbindService(serviceConnection)

まとめ

RemoteSharedFlowを用いることにより、バインドタイプのサービスを起動することができ、クライアントとサーバーとで、文字列データの通信(双方向)が行えることを確認しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?