Jelajahi Sumber

[core] Simplify and stabilize BaseService and AudioToSilkService (#2658)

* [core] Simplify and stabilize BaseService and AudioToSilkService

* fix

* api dump
Him188 2 tahun lalu
induk
melakukan
dafa25d611

+ 14 - 0
mirai-core-api/compatibility-validation/android/api/android.api

@@ -5438,6 +5438,20 @@ public final class net/mamoe/mirai/network/UnsupportedSmsLoginException : net/ma
 public final class net/mamoe/mirai/network/WrongPasswordException : net/mamoe/mirai/network/LoginFailedException {
 }
 
+public abstract interface class net/mamoe/mirai/spi/AudioToSilkService : net/mamoe/mirai/spi/BaseService {
+	public static final field Companion Lnet/mamoe/mirai/spi/AudioToSilkService$Companion;
+	public abstract fun convert (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+	public static fun getInstance ()Lnet/mamoe/mirai/spi/AudioToSilkService;
+}
+
+public final class net/mamoe/mirai/spi/AudioToSilkService$Companion {
+	public final fun getInstance ()Lnet/mamoe/mirai/spi/AudioToSilkService;
+}
+
+public abstract interface class net/mamoe/mirai/spi/BaseService {
+	public fun getPriority ()I
+}
+
 public abstract class net/mamoe/mirai/utils/AbstractBotConfiguration {
 	public fun <init> ()V
 	public final fun fileBasedDeviceInfo ()V

+ 14 - 0
mirai-core-api/compatibility-validation/jvm/api/jvm.api

@@ -5438,6 +5438,20 @@ public final class net/mamoe/mirai/network/UnsupportedSmsLoginException : net/ma
 public final class net/mamoe/mirai/network/WrongPasswordException : net/mamoe/mirai/network/LoginFailedException {
 }
 
+public abstract interface class net/mamoe/mirai/spi/AudioToSilkService : net/mamoe/mirai/spi/BaseService {
+	public static final field Companion Lnet/mamoe/mirai/spi/AudioToSilkService$Companion;
+	public abstract fun convert (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+	public static fun getInstance ()Lnet/mamoe/mirai/spi/AudioToSilkService;
+}
+
+public final class net/mamoe/mirai/spi/AudioToSilkService$Companion {
+	public final fun getInstance ()Lnet/mamoe/mirai/spi/AudioToSilkService;
+}
+
+public abstract interface class net/mamoe/mirai/spi/BaseService {
+	public fun getPriority ()I
+}
+
 public abstract class net/mamoe/mirai/utils/AbstractBotConfiguration {
 	public fun <init> ()V
 	public final fun fileBasedDeviceInfo ()V

+ 15 - 20
mirai-core-api/src/commonMain/kotlin/spi/AudioToSilkService.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 Mamoe Technologies and contributors.
  *
  * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@@ -9,17 +9,17 @@
 
 package net.mamoe.mirai.spi
 
-import io.ktor.utils.io.errors.*
-import net.mamoe.mirai.utils.*
-import kotlin.coroutines.cancellation.CancellationException
+import net.mamoe.mirai.utils.ExternalResource
+import net.mamoe.mirai.utils.runAutoClose
+import net.mamoe.mirai.utils.useAutoClose
+import net.mamoe.mirai.utils.withAutoClose
 import kotlin.jvm.JvmStatic
 
 /**
  * 将源音频文件转换为 silk v3 with tencent 格式
  *
  * @since 2.8.0
- */
-@MiraiExperimentalApi
+ */ // stable since 2.15
 public interface AudioToSilkService : BaseService {
     /**
      * implementation note:
@@ -35,24 +35,19 @@ public interface AudioToSilkService : BaseService {
      * @see [runAutoClose]
      * @see [useAutoClose]
      */
-    @Throws(IOException::class, CancellationException::class)
     public suspend fun convert(source: ExternalResource): ExternalResource
 
-    @MiraiExperimentalApi
-    public companion object : AudioToSilkService {
-        private val loader = SPIServiceLoader(object : AudioToSilkService {
-            override suspend fun convert(source: ExternalResource): ExternalResource = source
-        }, AudioToSilkService::class)
-
-        @Suppress("BlockingMethodInNonBlockingContext")
-        @Throws(IOException::class, CancellationException::class)
-        override suspend fun convert(source: ExternalResource): ExternalResource {
-            return loader.service.convert(source)
+    public companion object {
+        private val loader = SpiServiceLoader(AudioToSilkService::class) {
+            object : AudioToSilkService {
+                override suspend fun convert(source: ExternalResource): ExternalResource = source
+            }
         }
 
+        /**
+         * 获取当前实例
+         */
         @JvmStatic
-        public fun setService(service: AudioToSilkService) {
-            loader.service = service
-        }
+        public val instance: AudioToSilkService get() = loader.service
     }
 }

+ 52 - 13
mirai-core-api/src/commonMain/kotlin/spi/SPIServiceLoader.kt

@@ -9,32 +9,71 @@
 
 package net.mamoe.mirai.spi
 
-import net.mamoe.mirai.utils.MiraiExperimentalApi
+import kotlinx.atomicfu.locks.SynchronizedObject
+import kotlinx.atomicfu.locks.synchronized
 import net.mamoe.mirai.utils.MiraiLogger
-import kotlin.jvm.JvmField
+import net.mamoe.mirai.utils.lateinitMutableProperty
+import net.mamoe.mirai.utils.loadServices
 import kotlin.reflect.KClass
 
 /**
  * 基本 SPI 接口
  * @since 2.8.0
- */
-@MiraiExperimentalApi
+ */ // stable since 2.15
 public interface BaseService {
     /** 使用优先级, 值越小越先使用 */
-    public val priority: Int get() = 5
+    public val priority: Int get() = 0
 }
 
-@OptIn(MiraiExperimentalApi::class)
-internal expect class SPIServiceLoader<T : BaseService>(
-    defaultService: T,
+internal fun <T : BaseService> SpiServiceLoader(
     serviceType: KClass<T>,
-) {
-    @JvmField
-    var service: T
+    defaultImplementation: () -> T
+): SpiServiceLoader<T> {
+    return SpiServiceLoaderImpl(serviceType, defaultImplementation)
+}
+
+
+internal fun <T : BaseService> SpiServiceLoader(
+    serviceType: KClass<T>
+): SpiServiceLoader<T?> {
+    return SpiServiceLoaderImpl(serviceType, null)
+}
+
+internal interface SpiServiceLoader<T : BaseService?> {
+    val service: T
+}
 
-    fun reload()
+internal class SpiServiceLoaderImpl<T : BaseService?>(
+    private val serviceType: KClass<T & Any>,
+    defaultService: (() -> T)?
+) : SpiServiceLoader<T> {
+    private val defaultInstance: T? by lazy {
+        defaultService?.invoke()
+    }
+    private val lock = SynchronizedObject()
+
+    override val service: T get() = _service
+
+    private var _service: T by lateinitMutableProperty {
+        synchronized(lock) {
+            reloadAndSelect()
+        }
+    }
+
+    fun reload() {
+        synchronized(lock) {
+            _service = reloadAndSelect()
+        }
+    }
+
+    private fun reloadAndSelect(): T {
+        @Suppress("UNCHECKED_CAST")
+        return (loadServices(serviceType).minByOrNull { it.priority } ?: defaultInstance) as T
+    }
 
     companion object {
-        val SPI_SERVICE_LOADER_LOGGER: MiraiLogger
+        val SPI_SERVICE_LOADER_LOGGER: MiraiLogger by lazy {
+            MiraiLogger.Factory.create(SpiServiceLoader::class, "spi-service-loader")
+        }
     }
 }

+ 0 - 42
mirai-core-api/src/jvmBaseMain/kotlin/spi/SPIServiceLoader.kt

@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2023 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/dev/LICENSE
- */
-
-@file:OptIn(MiraiExperimentalApi::class)
-
-package net.mamoe.mirai.spi
-
-import net.mamoe.mirai.utils.MiraiExperimentalApi
-import net.mamoe.mirai.utils.MiraiLogger
-import java.util.*
-import kotlin.reflect.KClass
-
-
-internal actual class SPIServiceLoader<T : BaseService> actual constructor(
-    private val defaultService: T,
-    private val serviceType: KClass<T>
-) {
-    actual var service: T = defaultService
-
-    actual fun reload() {
-        val loader = ServiceLoader.load(serviceType.java)
-        service = loader.minByOrNull { it.priority } ?: defaultService
-    }
-
-
-    init {
-        reload()
-    }
-
-    actual companion object {
-        actual val SPI_SERVICE_LOADER_LOGGER: MiraiLogger by lazy {
-            MiraiLogger.Factory.create(SPIServiceLoader::class, "spi-service-loader")
-        }
-    }
-
-}

+ 0 - 39
mirai-core-api/src/nativeMain/kotlin/spi/SPIServiceLoader.kt

@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2023 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/dev/LICENSE
- */
-
-@file:OptIn(MiraiExperimentalApi::class)
-
-package net.mamoe.mirai.spi
-
-import net.mamoe.mirai.utils.MiraiExperimentalApi
-import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.loadService
-import kotlin.reflect.KClass
-
-internal actual class SPIServiceLoader<T : BaseService> actual constructor(
-    defaultService: T,
-    private val serviceType: KClass<T>
-) {
-    actual var service: T = defaultService
-
-    actual fun reload() {
-        service = loadService(serviceType) { service }
-    }
-
-    init {
-        reload()
-    }
-
-    actual companion object {
-        actual val SPI_SERVICE_LOADER_LOGGER: MiraiLogger by lazy {
-            MiraiLogger.Factory.create(SPIServiceLoader::class, "spi-service-loader")
-        }
-    }
-
-}

+ 2 - 2
mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 Mamoe Technologies and contributors.
  *
  * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@@ -109,7 +109,7 @@ internal class FriendImpl(
 
     override fun toString(): String = "Friend($id)"
 
-    override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = AudioToSilkService.convert(
+    override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = AudioToSilkService.instance.convert(
         resource
     ).useAutoClose { res ->
 

+ 3 - 3
mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 Mamoe Technologies and contributors.
  *
  * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@@ -311,7 +311,7 @@ internal abstract class CommonGroupImpl constructor(
     @Deprecated("use uploadAudio", replaceWith = ReplaceWith("uploadAudio(resource)"), level = DeprecationLevel.HIDDEN)
     @Suppress("OverridingDeprecatedMember", "DEPRECATION", "DEPRECATION_ERROR")
     override suspend fun uploadVoice(resource: ExternalResource): net.mamoe.mirai.message.data.Voice =
-        AudioToSilkService.convert(
+        AudioToSilkService.instance.convert(
             resource
         ).useAutoClose { res ->
             return bot.network.run {
@@ -359,7 +359,7 @@ internal abstract class CommonGroupImpl constructor(
         }.getOrThrow()
     }
 
-    override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = AudioToSilkService.convert(
+    override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = AudioToSilkService.instance.convert(
         resource
     ).useAutoClose { res ->
         return bot.network.run {