mirror of
https://mirror.skon.top/github.com/gkd-kit/gkd
synced 2026-04-30 13:51:02 +08:00
perf: ktor http server
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun String.runCommand(currentWorkingDir: File = file("./")): String {
|
||||
val byteOut = ByteArrayOutputStream()
|
||||
project.exec {
|
||||
workingDir = currentWorkingDir
|
||||
commandLine = this@runCommand.split("\\s".toRegex())
|
||||
standardOutput = byteOut
|
||||
errorOutput = ByteArrayOutputStream()
|
||||
fun String.runCommand(): String {
|
||||
val process = ProcessBuilder(split(" "))
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
val output = process.inputStream.bufferedReader().readText().trim()
|
||||
val exitCode = process.waitFor()
|
||||
if (exitCode != 0) {
|
||||
error("Command failed with exit code $exitCode: $output")
|
||||
}
|
||||
return String(byteOut.toByteArray()).trim()
|
||||
return output
|
||||
}
|
||||
|
||||
data class GitInfo(
|
||||
@@ -191,6 +190,7 @@ composeCompiler {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
|
||||
implementation(project(mapOf("path" to ":selector")))
|
||||
|
||||
|
||||
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
@@ -22,6 +22,10 @@
|
||||
-keep class li.songe.**{*;}
|
||||
-keep interface li.songe.**{*;}
|
||||
|
||||
# fix ktor error https://youtrack.jetbrains.com/issue/KTOR-7298
|
||||
# it should the bug of agp
|
||||
-keep class com.hjq.toast.** {*;}
|
||||
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
|
||||
-keep, allowobfuscation, allowoptimization @kotlinx.serialization.Serializable class * {*;}
|
||||
|
||||
@@ -49,6 +49,7 @@ import li.songe.gkd.util.OnCreate
|
||||
import li.songe.gkd.util.OnDestroy
|
||||
import li.songe.gkd.util.SERVER_SCRIPT_URL
|
||||
import li.songe.gkd.util.getIpAddressInLocalNetwork
|
||||
import li.songe.gkd.util.isPortAvailable
|
||||
import li.songe.gkd.util.keepNullJson
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.map
|
||||
@@ -78,9 +79,6 @@ class HttpService : Service(), OnCreate, OnDestroy {
|
||||
|
||||
private val httpServerPortFlow = storeFlow.map(scope) { s -> s.httpServerPort }
|
||||
|
||||
private var server: EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration>? =
|
||||
null
|
||||
|
||||
init {
|
||||
useLogLifecycle()
|
||||
useAliveFlow(isRunning)
|
||||
@@ -92,21 +90,28 @@ class HttpService : Service(), OnCreate, OnDestroy {
|
||||
if (storeFlow.value.autoClearMemorySubs) {
|
||||
deleteSubscription(LOCAL_HTTP_SUBS_ID)
|
||||
}
|
||||
httpServerFlow.value = null
|
||||
}
|
||||
|
||||
onCreated {
|
||||
httpNotif.notifyService(this)
|
||||
scope.launchTry(Dispatchers.IO) {
|
||||
httpServerPortFlow.collect { port ->
|
||||
server?.stop()
|
||||
server = try {
|
||||
httpServerFlow.value?.stop()
|
||||
httpServerFlow.value = null
|
||||
if (!isPortAvailable(port)) {
|
||||
toast("端口 $port 被占用, 请更换后重试")
|
||||
stopSelf()
|
||||
return@collect
|
||||
}
|
||||
httpServerFlow.value = try {
|
||||
scope.createServer(port).apply { start() }
|
||||
} catch (e: Exception) {
|
||||
toast("HTTP服务启动失败:${e.stackTraceToString()}")
|
||||
LogUtils.d("HTTP服务启动失败", e)
|
||||
null
|
||||
}
|
||||
if (server == null) {
|
||||
toast("HTTP服务启动失败,您可以尝试切换端口后重新启动")
|
||||
if (httpServerFlow.value == null) {
|
||||
stopSelf()
|
||||
return@collect
|
||||
}
|
||||
@@ -117,6 +122,7 @@ class HttpService : Service(), OnCreate, OnDestroy {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val httpServerFlow = MutableStateFlow<ServerType?>(null)
|
||||
val isRunning = MutableStateFlow(false)
|
||||
val localNetworkIpsFlow = MutableStateFlow(emptyList<String>())
|
||||
fun stop() {
|
||||
@@ -130,6 +136,9 @@ class HttpService : Service(), OnCreate, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
typealias ServerType = EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration>
|
||||
|
||||
|
||||
@Serializable
|
||||
data class RpcOk(
|
||||
val message: String? = null,
|
||||
|
||||
@@ -136,6 +136,11 @@ fun AdvancedPage() {
|
||||
httpServerPort = newPort
|
||||
)
|
||||
showEditPortDlg = false
|
||||
if (HttpService.httpServerFlow.value != null) {
|
||||
toast("已更新, 重启服务")
|
||||
} else {
|
||||
toast("已更新")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
@@ -196,7 +201,8 @@ fun AdvancedPage() {
|
||||
ShizukuFragment()
|
||||
}
|
||||
|
||||
val httpServerRunning by HttpService.isRunning.collectAsState()
|
||||
val server by HttpService.httpServerFlow.collectAsState()
|
||||
val httpServerRunning = server != null
|
||||
val localNetworkIps by HttpService.localNetworkIpsFlow.collectAsState()
|
||||
|
||||
Text(
|
||||
@@ -252,14 +258,14 @@ fun AdvancedPage() {
|
||||
}
|
||||
Switch(
|
||||
checked = httpServerRunning,
|
||||
onCheckedChange = vm.viewModelScope.launchAsFn<Boolean> {
|
||||
onCheckedChange = throttle(fn = vm.viewModelScope.launchAsFn<Boolean> {
|
||||
if (it) {
|
||||
requiredPermission(context, notificationState)
|
||||
HttpService.start()
|
||||
} else {
|
||||
HttpService.stop()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.songe.gkd.util
|
||||
|
||||
import java.net.NetworkInterface
|
||||
import java.net.ServerSocket
|
||||
|
||||
fun getIpAddressInLocalNetwork(): List<String> {
|
||||
val networkInterfaces = try {
|
||||
@@ -18,3 +19,18 @@ fun getIpAddressInLocalNetwork(): List<String> {
|
||||
}
|
||||
return localAddresses.toList()
|
||||
}
|
||||
|
||||
|
||||
fun isPortAvailable(port: Int): Boolean {
|
||||
var serverSocket: ServerSocket? = null
|
||||
return try {
|
||||
serverSocket = ServerSocket(port)
|
||||
serverSocket.reuseAddress = true
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
} finally {
|
||||
serverSocket?.close()
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,3 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
dependencies {
|
||||
classpath(libs.android.gradle)
|
||||
classpath(libs.kotlin.gradle.plugin)
|
||||
classpath(libs.kotlin.serialization)
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.google.ksp) apply false
|
||||
|
||||
@@ -30,22 +15,6 @@ plugins {
|
||||
alias(libs.plugins.rikka.refine) apply false
|
||||
}
|
||||
|
||||
// can not work with Kotlin Multiplatform
|
||||
// https://youtrack.jetbrains.com/issue/KT-33191/
|
||||
//tasks.register<Delete>("clean").configure {
|
||||
// delete(rootProject.buildDir)
|
||||
//}
|
||||
|
||||
project.gradle.taskGraph.whenReady {
|
||||
allTasks.forEach { task ->
|
||||
// error: The binary version of its metadata is 1.8.0, expected version is 1.6.0.
|
||||
// I don't know how to solve it, so just disable these tasks
|
||||
if (task.name.contains("lintAnalyzeDebug") || task.name.contains("lintVitalAnalyzeRelease")) {
|
||||
task.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://kotlinlang.org/docs/js-project-setup.html#use-pre-installed-node-js
|
||||
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().download =
|
||||
|
||||
@@ -3,8 +3,6 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.debug.obsoleteApi=true
|
||||
kotlin.code.style=official
|
||||
# https://youtrack.jetbrains.com/issue/KTOR-7298
|
||||
android.suppressUnsupportedCompileSdk=35
|
||||
android_compileSdk=35
|
||||
android_targetSdk=35
|
||||
android_buildToolsVersion=35.0.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[versions]
|
||||
kotlin = "2.0.21"
|
||||
ksp = "2.0.21-1.0.25"
|
||||
android = "8.5.2"
|
||||
android = "8.7.2"
|
||||
compose = "1.7.5"
|
||||
rikka = "4.4.0"
|
||||
room = "2.6.1"
|
||||
@@ -12,9 +12,7 @@ coil = "2.7.0"
|
||||
shizuku = "13.1.5"
|
||||
|
||||
[libraries]
|
||||
kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
kotlin_serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
|
||||
kotlin_stdlib_common = { module = "org.jetbrains.kotlin:kotlin-stdlib-common", version.ref = "kotlin" }
|
||||
kotlin_stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
kotlin_test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
kotlinx_serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.3" }
|
||||
ktor_server_core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
|
||||
@@ -24,7 +22,6 @@ ktor_client_core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||
ktor_client_okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
||||
ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
|
||||
ktor_serialization_kotlinx_json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||
android_gradle = { module = "com.android.tools.build:gradle", version.ref = "android" }
|
||||
compose_ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
|
||||
compose_ui_graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "compose" }
|
||||
compose_animation = { module = "androidx.compose.animation:animation", version.ref = "compose" }
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
@@ -21,7 +18,7 @@ kotlin {
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib.common)
|
||||
implementation(libs.kotlin.stdlib)
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
||||
@@ -8,9 +8,15 @@ pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
maven("https://jitpack.io")
|
||||
maven("https://plugins.gradle.org/m2/")
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class)
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
@@ -22,7 +26,7 @@ kotlin {
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib.common)
|
||||
implementation(libs.kotlin.stdlib)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user