Android前台服务
Android前台服务用于执行用户可察觉的操作。
前台服务显示状态栏通知,让用户知道您的 应用正在前台执行任务并消耗系统资源。
文件服务启动和停止
在JVM上,通过守护线程来启动文件服务。
class DesktopServerViewModel : ServerViewModel, ViewModel() {
...
override fun startFileServer(httpServerConfig: HttpFileServerConfig?) {
startServerDaemon(httpServerConfig)
}
override fun stopFileServer() {
stopServerDaemon()
}
}
actual fun getMsgViewModel(): ServerViewModel = DesktopServerViewModel()
class ServerDaemon(private val httpServerConfig: HttpFileServerConfig) : Thread() {
override fun run() {
bootStrapServer.startServer(httpServerConfig)
}
companion object {
private lateinit var serverDaemon: ServerDaemon
fun startServerDaemon(httpServerConfig: HttpFileServerConfig?) {
httpServerConfig ?: run {
LogTool.e("不能加载配置文件")
return
}
serverDaemon = ServerDaemon(httpServerConfig).also {
it.isDaemon = true
it.name = "Server-daemon-thread"
}.apply {
start()
}
}
fun stopServerDaemon() {
try {
bootStrapServer.stopServer()
if (serverDaemon.isAlive) {
serverDaemon.interrupt()
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
在Android中,为了避免由于界面退出或锁屏后被系统杀死进程导致文件服务中断,使用前台服务实现。
@Suppress("DEPRECATION")
class AndroidServerViewModel : ViewModel(), ServerViewModel {
...
@RequiresApi(VERSION_CODES.O)
override fun startFileServer(httpServerConfig: HttpFileServerConfig?) {
FileServerApp.app.startForegroundService(fileServerIntent.also { //启动前台服务
it.putExtra("serverPort", httpServerConfig?.serverPort)
it.putExtra("fileDirectory", httpServerConfig?.fileDirectory)
})
}
override fun stopFileServer() {
FileServerApp.app.stopService(fileServerIntent) // 停止前台服务。
}
...
private companion object {
...
val fileServerIntent = Intent().also {
it.setClass(FileServerApp.app.applicationContext, FileServerService::class.java)
}
}
}
actual fun getMsgViewModel(): ServerViewModel = AndroidServerViewModel()
创建FileServerService,并声明为前台服务。声明前台服务,需要在AndroidManifest.xml中声明,并申请权限。 权限相关可参阅https://blog.youkuaiyun.com/luckyion/article/details/141305213
class FileServerService : Service() {
override fun onBind(intent: Intent): IBinder? = null
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
getSystemService(NotificationManager::class.java).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(
NotificationChannel(
MY_CHANNEL_ID,
MY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
)
)
}
}
try {
startForeground(MY_NOTIFICATION_ID, getNotification(this))
LogTool.i("startForeground")
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
LogTool.i("onStartCommand")
startServer(
HttpFileServerConfig(
intent?.getIntExtra("serverPort", 80)!!,
intent.getStringExtra("fileDirectory")!!
)
)
return super.onStartCommand(intent, flags, startId)
}
private fun startServer(httpServerConfig: HttpFileServerConfig) {
LogTool.i("startServer")
startServerDaemon(httpServerConfig) // 启动文件服务线程
}
private fun stopServer() {
stopServerDaemon() // 停止文件服务线程
}
override fun onDestroy() {
stopServer()
super.onDestroy()
}
@RequiresApi(Build.VERSION_CODES.O)
private fun getNotification(context: Context) = // 前台服务通知,创建前台服务,必须发送一个通知。
Notification.Builder(context, MY_CHANNEL_ID)
.setShowWhen(true)
.setSmallIcon(R.drawable.explore)
.setContentTitle("服务服务器")
.setContentText("服务已启动").also {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
it.setChannelId(MY_CHANNEL_ID)
}
}.build()
private companion object {
private const val MY_CHANNEL_ID = "File-server-foreground-service-channel"
private const val MY_CHANNEL_NAME = "File-server-foreground-service-channel-name"
private const val MY_NOTIFICATION_ID = 10086
}
}