しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

Kotlinでサービスを使ってみる

Androidのサービスという仕組みを試してみました。
サービスとはバックグラウンドで処理を行う時などに使う機能で、ActivityからUIを除いたような動作をします。

まずはプロジェクトを作成してからServiceを追加します。

名前などを適当につけて作成します。

作成すると下のようなクラスが作られます。

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }
}

AndroidManifest.xmlには下のように追加されます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.myapplication">
    <application>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

    </application>
</manifest>

次はサービスを実行してみます。
実行前にログを出力する処理だけ追加します。

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()

        Log.d("test", "MyService onCreate")
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("test", "MyService onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }
    
    override fun onDestroy() {
        super.onDestroy()

        Log.d("test", "MyService onDestroy")
    }
}

MainActivityから下のように呼び出しました。

startService(Intent(this, MyService::class.java))

無事にログが表示されました。
サービスで実行したい処理などはonStartCommandに記述します。
onStartCommandの戻り値はサービス強制終了時の挙動方法を返します。

次はサービスを終了してみます。
終了はActivityでstopServiceメソッドを呼び出すだけです。
引数には終了したいServiceのIntentを入れます。
引数のIntentはstartで渡したインスタンスと違うものでも構いません。

stopService(Intent(this, MyService::class.java))

Service側でstopSelfを呼び出す事でも終了できます。

stopSelf()

次はサービスにバインドして利用してみます。
そうする事でActivityからサービスを操作したり値を受け取ったりする事ができるようになります。
startServiceと違ってバックグラウンドで動き続けるわけではないので注意が必要です。
ずっと動かしたいはstartServiceと併用する必要があります。

まずはMyServiceを下のように修正します。
IncomingHandlerはActivityとやり取りするためのクラスです。
bindした時はonStartCommandは呼ばれず、代わりにonBindが呼ばれます。

class MyService : Service() {
    lateinit var messenger: Messenger

    override fun onBind(intent: Intent?): IBinder? {
        messenger = Messenger(IncomingHandler(this))
        return messenger.binder
    }

    internal class IncomingHandler(context: Context, private val applicationContext: Context = context.applicationContext) : Handler() {
        override fun handleMessage(msg: Message) {
            Log.d("test", "IncomingHandler")
        }
    }
}

次はActivity側も下のように修正します。
Serviceとの連携に必要なconnectionというインスタンス変数を作成します。

class MainActivity : AppCompatActivity() {
    private var messender: Messenger? = null
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            messender = Messenger(service)
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
        }
    }
}

Serviceの起動はActivityでbindServiceを呼び出します。
第2引数に先程作ったconnectionを使います。

bindService(intent, connection, Context.BIND_AUTO_CREATE)

bind後にactivityからメッセージを送る方法は下の通りです。
これによってIncomingHandlerのhandleMessageが呼ばれます。

messender?.send(Message.obtain(null, 0))

参考URL

サービスの概要  |  Android デベロッパー  |  Android Developers

【Kotlin研修10日目】バックグラウンド処理の実装、サービスのライフサイクル - Qiita