LoginSignup
9
15

More than 3 years have passed since last update.

AndroidでServiceと通信する(Kotlinサンプル)

Posted at

サンプルコード

ActivityとServiceを1つずつ立ち上げてmessengerでやり取りするサンプルです。
意外とkotlinのサンプルがなかったので、書いてみました。
走り書きですが、よければコピペして使ってください。
※Qiita上でベタベタと切り貼りしたので、貼り付けるだけだと動かないかもしれません。

この中身を解説していきます。

コード全量はこちら。
MainActivity.kt
package com.satken.randomwikireader

import android.content.ComponentName
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.content.Intent
import android.content.ServiceConnection
import android.widget.Button
import android.os.*
import java.lang.ref.WeakReference

const val MSG_ACTIVITY_TO_SERVICE = 1
const val MSG_SERVICE_TO_ACTIVITY = 2

class MainActivity : AppCompatActivity() {
    private var bound = false
    private var mActivityMessenger: Messenger? = null
    private var mServiceMessenger: Messenger? = null
    private var mActivityHandler: ActivityHandler? = null

    private val mConnection = object: ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
            mServiceMessenger = Messenger(binder)
            try {
                val msg = Message.obtain(null, MSG_ACTIVITY_TO_SERVICE, 0, 0)
                msg.replyTo = mActivityMessenger
                mServiceMessenger!!.send(msg)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mServiceMessenger = null
            bound = false
        }
    }

    internal class ActivityHandler(
        activity: MainActivity
    ) : Handler() {
        //define weak reference to activity
        private val mActivity = WeakReference<MainActivity>(activity)

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SERVICE_TO_ACTIVITY ->{
                    //something
                }
                else -> super.handleMessage(msg)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mActivityHandler = ActivityHandler(this)
        mActivityMessenger = Messenger(mActivityHandler)

        findViewById<Button>(R.id.start).setOnClickListener {
            if(bound) {
                try {
                    val msg = Message.obtain(null, MSG_STOP_UTTERANCE, 0, 0)
                    msg.replyTo = mActivityMessenger
                    mServiceMessenger!!.send(msg)
                } catch (e: RemoteException) {
                    e.printStackTrace()
                }
            }

        }

        //start service
        Intent(this, MyService::class.java).also {
            it.action = action
            //use startForgroundService in Android-O
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(it)
                return
            }
            startService(it)
        }
    }

    override fun onStop() {
        super.onStop()
        //unbind from the service
        if(bound){
            unbindService(mConnection)
            bound = false
        }
    }

    override fun onStart(){
        super.onStart()
        //bind to the service
        Intent(this, MyService::class.java).also{ intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }
}

MyService.kt
package com.satken.randomwikireader

import android.app.Service
import android.content.Intent
import android.os.*
import java.lang.ref.WeakReference

class MyService: Service() {
    private var mServiceMessenger: Messenger? = null

    internal class ServiceHandler(
        service: MyService
    ) : Handler() {
        private val mService = WeakReference<MyService>(service)

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_ACTIVITY_TO_SERVICE ->{
                    //do something

                    //return some value
                    try {
                        val msg2 = Message.obtain(null, MSG_SERVICE_TO_ACTIVITY, 0, 0)
                        msg.replyTo.send(msg2)
                    } catch (e: RemoteException) {
                        e.printStackTrace()
                    }
                }
                else -> super.handleMessage(msg)
            }
        }
    }

    override fun onBind(intent: Intent?): IBinder {
        mServiceMessenger = Messenger(ServiceHandler(this))
        return mServiceMessenger!!.binder
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return Service.START_NOT_STICKY
    }
}


Serviceを起動する

サービスを起動する時は、Intentを使って以下のように呼び出します。
2行目から6行目は、別になくても大丈夫です。
API level26 以降はstartForegroundServiceが使えます。

MainActivity.kt
Intent(this, MyService::class.java).also {
    it.action = action
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(it)
        return
    }
    startService(it)
}

Activityとの関連付け

起動したサービスに対して何かActivityから情報を伝える方法として、Messengerを使うことができます。
他にも方法はありますが、これが手軽です。

Connectionの定義

ActivityにはConnectionのインスタンスを作成しておきます。
サービスをbindする時はこのインスタンスをリスナーとして指定します。

MainActivity.kt
private val mConnection = object: ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
        mServiceMessenger = Messenger(binder)
        bound = true
    }

    override fun onServiceDisconnected(name: ComponentName?) {
        mServiceMessenger = null
        bound = false
    }
}

onServiceConnectedでは、IBinderからMessengerを作成しています。
ここで取得したmServiceMessengerは後で相互に通信する時に使います。

Bind

bindするタイミングはonStartがよさそうです。

MainActivity.kt
override fun onStart(){
    super.onStart()
    //bind to the service
    Intent(this, MyService::class.java).also{ intent ->
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
    }
}

Serviceとの通信

Activity→Service

通信を受ける側では、ハンドラークラスを定義し、onBindでインスタンスを作ります。

MyService.kt
internal class ServiceHandler(
    service: MyService
) : Handler() {
     private val mService = WeakReference<TTSService>(service)

     override fun handleMessage(msg: Message) {
        when (msg.what) {
           MSG_ACTIVITY_TO_SERVICE ->{

            }
            else -> super.handleMessage(msg)
        }
    }
}

override fun onBind(intent: Intent?): IBinder {
    mServiceMessenger = Messenger(ServiceHandler(this))
    return mServiceMessenger!!.binder
}

通信を送る側では、さきほどonServiceConnectedで取得したMessengerに対してMessageを送ります。
MessageにreplyToをして、返信先のMessengerを指定できます。
自身のActivityで作ったMessengerを指定しておきましょう。

MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    mActivityMessenger = Messenger(ActivityHandler(this))
}

fun sendMesasge(){
    try {
        val msg = Message.obtain(null, MSG_ACTIVITY_TO_SERVICE, 0, 0)
        msg.replyTo = mActivityMessenger
        mServiceMessenger!!.send(msg)
    } catch (e: RemoteException) {
        e.printStackTrace()
    }
}

Service→Activity

サービス側で何か処理をして、結果をActivityに返すには同様にMessengerを使用します。
さきほどmsgにreplytoを指定したので、そこに対して送ればAcitivityに戻ります。

service.ky
override fun handleMessage(msg: Message) {
    try {
        val msg2 = Message.obtain(null, MSG_SERVICE_TO_ACTIVITY, 0, 0)
        msg.replyTo.send(msg2)
    } catch (e: RemoteException) {
        e.printStackTrace()
    }
}

Unbind

bindしたサービスはonStopとかでunbindしておきましょう。

MainActivity.kt
override fun onStop() {
    super.onStop()
    //unbind from the service
    if(bound){
        unbindService(mConnection)
        bound = false
    }
}
9
15
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
9
15