基于Kotlin Multiplatform实现静态文件服务器(四)

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
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值