Android 四大组件之一 Service

Android 四大组件之一 Service

Android Service是具有生命周期的,具有三种。

  • start
  • bind
  • start 和 bind 混合

混合是生命周期问题

[startService()] → onCreate() → onStartCommand() → running
[bindService()] → onBind() → bound
[unbindService()] → onUnbind() → still running
[stopService()] → onDestroy()

不管是调用stopService还是unbindService谁最后调用,service才被销毁。

service 使用

定义
HelloService.kt

internal class HelloService internal constructor() : LifecycleService() {

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate...")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId).apply {
            Log.i(TAG, "onStartCommand intent: $intent, flags: $flags, startId: $startId value: $this")
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        super.onBind(intent)
        Log.i(TAG, "onBind intent: $intent...")
        return HelloBinder()
    }

    override fun onRebind(intent: Intent?) {
        Log.i(TAG, "onRebind -> intent: $intent")
        super.onRebind(intent)
    }
    override fun onUnbind(intent: Intent?): Boolean {
        return super.onUnbind(intent).apply {
            Log.i(TAG, "onUnbind -> intent: $intent value: $this")
        }
    }

    override fun onDestroy() {
        Log.i(TAG, "onDestroy...")
        super.onDestroy()
    }

    internal class HelloBinder : Binder() {
        internal fun getHello(): String {
            return "Hello: ${Random.nextInt(until = 1000)}"
        }
    }
}

ServiceScreen.kt

@Composable
internal fun ServiceScreen(
    modifier: Modifier,
    snackBarHostState: SnackbarHostState
) {
    val context: Context = LocalContext.current
    val coroutineScope: CoroutineScope = rememberCoroutineScope()
    var helloBinder: HelloService.HelloBinder? by remember {
        mutableStateOf(value = null)
    }
    val connection: ServiceConnection by remember {
        mutableStateOf(value = object : ServiceConnection {
            override fun onServiceConnected(
                name: ComponentName?,
                service: IBinder?
            ) {
                helloBinder = service as? HelloService.HelloBinder
                Log.i(TAG, "onServiceConnected -> name: $name, service: $service")
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                Log.i(TAG, "onServiceDisconnected -> name: $name")
            }
        })
    }
    Column(
        modifier = modifier.fillMaxSize()
    ) {
        Text(
            text = "启动服务",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    // context.startService(Intent(context, HelloService::class.java))
                    context.bindService(
                        Intent(context, HelloService::class.java),
                        connection,
                        Context.BIND_AUTO_CREATE
                    )
                    val message = helloBinder?.getHello()
                    Log.i(TAG, "ServiceScreen -> message: $message")
                },
            color = Color.White
        )

        Text(
            text = "关闭服务",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    // context.stopService(Intent(context, HelloService::class.java))
                    context.unbindService(connection)
                },
            color = Color.White
        )
    }
}

注册

<service android:name=".service.HelloService"/>

OnRebind

onRebind方法在Service没有销毁的时候,再次被绑定时触发
onStart->onBind->onUnbind->onBind(触发)->onUnbind->stopService->onDestroy
HelloService.kt

private const val TAG: String = "HelloService"

internal class HelloService internal constructor() : LifecycleService() {

    companion object {
        internal fun bindService(context: Context, connection: ServiceConnection){
            val isSuccess: Boolean = context.bindService(Intent(context, HelloService::class.java), connection, Context.BIND_AUTO_CREATE)
            Log.i(TAG, "bindService isSuccess: $isSuccess")
        }
        internal fun unbindService(context: Context, connection: ServiceConnection) {
            context.unbindService(connection)
        }
        internal fun startService(context: Context) {
            context.startService(Intent(context, HelloService::class.java))
        }
        internal fun stopService(context: Context) {
            context.stopService(Intent(context, HelloService::class.java))
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate...")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId).apply {
            Log.i(TAG, "onStartCommand intent: $intent, flags: $flags, startId: $startId value: $this")
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        super.onBind(intent)
        Log.i(TAG, "onBind intent: $intent...")
        return HelloBinder()
    }

    override fun onRebind(intent: Intent?) {
        Log.i(TAG, "onRebind -> intent: $intent")
        super.onRebind(intent)
    }
    override fun onUnbind(intent: Intent?): Boolean {
        return true
        // return super.onUnbind(intent).apply {
        //     Log.i(TAG, "onUnbind -> intent: $intent value: $this")
        // }
    }

    override fun onDestroy() {
        Log.i(TAG, "onDestroy...")
        super.onDestroy()
    }

    internal class HelloBinder : Binder() {
        internal fun getHello(): String {
            return "Hello: ${Random.nextInt(until = 1000)}"
        }
    }
}

ServiceScreen.kt

package edu.tyut.webviewlearn.ui.screen

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import edu.tyut.webviewlearn.service.HelloService
import edu.tyut.webviewlearn.ui.theme.RoundedCornerShape10
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

private const val TAG: String = "ProviderScreen"

@Composable
internal fun ServiceScreen(
    modifier: Modifier,
    snackBarHostState: SnackbarHostState
) {
    val context: Context = LocalContext.current
    val coroutineScope: CoroutineScope = rememberCoroutineScope()
    var helloBinder: HelloService.HelloBinder? by remember {
        mutableStateOf(value = null)
    }
    val connection: ServiceConnection by remember {
        mutableStateOf(value = object : ServiceConnection {
            override fun onServiceConnected(
                name: ComponentName?,
                service: IBinder?
            ) {
                helloBinder = service as? HelloService.HelloBinder
                Log.i(TAG, "onServiceConnected -> name: $name, service: $service")
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                Log.i(TAG, "onServiceDisconnected -> name: $name")
            }
        })
    }
    Column(
        modifier = modifier.fillMaxSize()
    ) {
        Text(
            text = "启动服务",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    // context.startService(Intent(context, HelloService::class.java))
                    HelloService.bindService(context, connection)
                    HelloService.startService(context)
                    val message = helloBinder?.getHello()
                    Log.i(TAG, "ServiceScreen -> message: $message")
                },
            color = Color.White
        )

        Text(
            text = "关闭服务",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    // context.stopService(Intent(context, HelloService::class.java))
                    context.unbindService(connection)
                },
            color = Color.White
        )
    }
}

跨进程通信

Service可以跨进程通信。

server app:

声明

<permission android:name="edu.tyut.webviewlearn.helloService"/>
<application
	<service android:name=".service.HelloService"
android:exported="true"
android:permission="edu.tyut.webviewlearn.helloService" />
</application>

client app:

声明

<uses-permission android:name="edu.tyut.webviewlearn.helloService"/>

使用
start方式,这种方式仅允许前台启动,否则android.app.BackgroundServiceStartNotAllowedException

@Composable
internal fun ServiceScreen(
    navHostController: NavHostController,
    snackBarHostState: SnackbarHostState,
){
    val context: Context = LocalContext.current
    Column(
        modifier = Modifier.fillMaxSize()
    ){
        Text(
            text = "连接Service",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    context.startService(Intent().setClassName("edu.tyut.webviewlearn", "edu.tyut.webviewlearn.service.HelloService"))
                },
            color = Color.White
        )
        Text(
            text = "断开Service",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    context.stopService(Intent().setClassName("edu.tyut.webviewlearn", "edu.tyut.webviewlearn.service.HelloService"))
                },
            color = Color.White
        )
    }
}

bind方式

package edu.tyut.helloktorfit.ui.screen

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.navigation.NavHostController
import edu.tyut.helloktorfit.ui.theme.RoundedCornerShape10

private const val TAG: String = "ServiceScreen"

@Composable
internal fun ServiceScreen(
    navHostController: NavHostController,
    snackBarHostState: SnackbarHostState,
){
    val context: Context = LocalContext.current
    val connection: ServiceConnection by remember {
        mutableStateOf(value = object : ServiceConnection {
            override fun onServiceConnected(
                name: ComponentName?,
                service: IBinder?
            ) {
                // helloBinder = service as? HelloService.HelloBinder
                Log.i(TAG, "onServiceConnected -> name: $name, service: $service")
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                Log.i(TAG, "onServiceDisconnected -> name: $name")
            }
        })
    }
    Column(
        modifier = Modifier.fillMaxSize()
    ){
        Text(
            text = "连接Service",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    val isSuccess: Boolean = context.bindService(
                        Intent().setClassName(
                            "edu.tyut.webviewlearn",
                            "edu.tyut.webviewlearn.service.HelloService"
                        ), connection, Context.BIND_AUTO_CREATE
                    )
                    Log.i(TAG, "ServiceScreen -> isSuccess: $isSuccess")
                    // context.startService(Intent().setClassName("edu.tyut.webviewlearn", "edu.tyut.webviewlearn.service.HelloService"))
                },
            color = Color.White
        )
        Text(
            text = "断开Service",
            Modifier
                .padding(top = 10.dp)
                .background(color = Color.Black, shape = RoundedCornerShape10)
                .padding(all = 5.dp)
                .clickable {
                    // context.stopService(Intent().setClassName("edu.tyut.webviewlearn", "edu.tyut.webviewlearn.service.HelloService"))
                    context.unbindService(connection)
                },
            color = Color.White
        )
    }
}

当服务启动时,才能成功绑定。

通信

你可以使用Messager、AIDL实现跨进程 通信。

posted @ 2025-07-07 22:21  爱情丶眨眼而去  阅读(10)  评论(0)    收藏  举报