Vivek Modi
Vivek Modi

Reputation: 7291

How to use service in jetpack compose

I want to use service in jetpack compose. I am trying to bind services in compose app but it always return null to me. I tried this stack overflow. But it didn't work to me. Anyone guide me on this ?

import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Binder
import android.os.IBinder
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext

@Composable
inline fun <reified BoundService : Service, reified BoundServiceBinder : Binder> rememberBoundLocalService(
    crossinline getService: @DisallowComposableCalls BoundServiceBinder.() -> BoundService,
): BoundService? {
    val context: Context = LocalContext.current
    var boundService: BoundService? by remember(context) { mutableStateOf(null) }
    val serviceConnection: ServiceConnection = remember(context) {
        object : ServiceConnection {
            override fun onServiceConnected(className: ComponentName, service: IBinder) {
                boundService = (service as BoundServiceBinder).getService()
            }

            override fun onServiceDisconnected(arg0: ComponentName) {
                boundService = null
            }
        }
    }
    DisposableEffect(context, serviceConnection) {
        context.bindService(Intent(context, BoundService::class.java), serviceConnection, Context.BIND_AUTO_CREATE)
        onDispose { context.unbindService(serviceConnection) }
    }
    return boundService
}

BluetoothService.kt

class BluetoothService : Service() {

    companion object {
        const val ACTION_GATT_CONNECTED =
            "com.abc.app.ACTION_GATT_CONNECTED"
        const val ACTION_GATT_DISCONNECTED =
            "com.abc.app.ACTION_GATT_DISCONNECTED"

        private const val STATE_DISCONNECTED = 0
        private const val STATE_CONNECTED = 2
    }

    private var connectionState = STATE_DISCONNECTED
    private var bluetoothAdapter: BluetoothAdapter? = null
    private val binder = LocalBinder()
    private var bluetoothGatt: BluetoothGatt? = null
    private val bluetoothGattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // successfully connected to the GATT Server
                connectionState = STATE_CONNECTED
                broadcastUpdate(ACTION_GATT_CONNECTED)
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // disconnected from the GATT Server
                connectionState = STATE_DISCONNECTED
                broadcastUpdate(ACTION_GATT_DISCONNECTED)
            }
        }
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }

    inner class LocalBinder : Binder() {
        fun getService(): BluetoothService {
            return this@BluetoothService
        }
    }

    fun initialize(adapter: BluetoothAdapter?): Boolean {
        bluetoothAdapter = adapter
        if (bluetoothAdapter == null) {
            logE(">> Unable to obtain a BluetoothAdapter.")
            return false
        }
        return true
    }

    fun connect(address: String): Boolean {
        bluetoothAdapter?.let { adapter ->
            try {
                val device = adapter.getRemoteDevice(address)
                // connect to the GATT server on the device
                bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
                return true
            } catch (exception: IllegalArgumentException) {
                logE("Device not found with provided address.")
                return false
            }
        } ?: run {
            logE("BluetoothAdapter not initialized")
            return false
        }
    }

    override fun onUnbind(intent: Intent?): Boolean {
        close()
        return super.onUnbind(intent)
    }

    private fun close() {
        bluetoothGatt?.let { gatt ->
            gatt.close()
            bluetoothGatt = null
        }
    }

    private fun broadcastUpdate(action: String) {
        val intent = Intent(action)
        sendBroadcast(intent)
    }
}

I am trying to use bluetoothService it always return null when I check in the condition

BluetoothConnectionContentStateful.kt

@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun BluetoothConnectionContentStateful(
    context: Context = LocalContext.current,
    viewModel: BloodPressurePairViewModel = getViewModel(),
    router: Router = get()
) {
    val activity = context as ComponentActivity
    val bluetoothService = rememberBoundLocalService<BluetoothService, BluetoothService.LocalBinder> { getService() }
    val scanDeviceList = viewModel.scanLeDevices.collectAsStateWithLifecycle()
    if (bluetoothService != null) {
        if (!bluetoothService.initialize(rememberPairScreenState.bluetoothAdapter)) {
            activity.logE(">> Unable to initialize Bluetooth")
        } else {
            activity.logE(">> initialize Bluetooth")
        }
    }else{
        activity.logE(">> bluetoothService null")
    }
}

bluetoothService always return null. Any idea about that?

Upvotes: 2

Views: 4651

Answers (1)

Devrath
Devrath

Reputation: 42854

  • It is not a good practice to bind the composable with service
  • Composable should only involve the presentation layer that listeners for the states in the view model
  • You can bind the composable to ViewModel or a repository and listen to the states in composable

In this project I can calling a API as a service, you can substitute with your service - Github link of source code and implementation

Upvotes: 0

Related Questions