diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt index 760c7d72..08c95320 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt @@ -10,7 +10,6 @@ import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.isActive -import libv2ray.Libv2ray import java.io.IOException import java.net.InetSocketAddress import java.net.Socket @@ -41,21 +40,6 @@ object SpeedtestManager { return time } - /** - * Measures the real ping time using the V2Ray library. - * - * @param config The configuration string for the V2Ray library. - * @return The ping time in milliseconds, or -1 if the ping failed. - */ - fun realPing(config: String): Long { - return try { - Libv2ray.measureOutboundDelay(config, SettingsManager.getDelayTestUrl()) - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to measure outbound delay", e) - -1L - } - } - /** * Measures the time taken to establish a TCP connection to a given URL and port. * @@ -160,14 +144,4 @@ object SpeedtestManager { return "(${country ?: "unknown"}) ${ip ?: "unknown"}" } - - /** - * Gets the version of the V2Ray library. - * - * @return The version of the V2Ray library. - */ - fun getLibVersion(): String { - return Libv2ray.checkVersionX() - } - } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayNativeManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayNativeManager.kt new file mode 100644 index 00000000..9d46279e --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayNativeManager.kt @@ -0,0 +1,91 @@ +package com.v2ray.ang.handler + +import android.content.Context +import android.util.Log +import com.v2ray.ang.AppConfig +import com.v2ray.ang.util.Utils +import go.Seq +import libv2ray.CoreCallbackHandler +import libv2ray.CoreController +import libv2ray.Libv2ray +import java.util.concurrent.atomic.AtomicBoolean + +/** + * V2Ray Native Library Manager + * + * Thread-safe singleton wrapper for Libv2ray native methods. + * Provides initialization protection and unified API for V2Ray core operations. + */ +object V2RayNativeManager { + private val initialized = AtomicBoolean(false) + + /** + * Initialize V2Ray core environment. + * This method is thread-safe and ensures initialization happens only once. + * Subsequent calls will be ignored silently. + * + */ + fun initCoreEnv(context: Context?) { + if (initialized.compareAndSet(false, true)) { + try { + Seq.setContext(context?.applicationContext) + val assetPath = Utils.userAssetPath(context) + val deviceId = Utils.getDeviceIdForXUDPBaseKey() + Libv2ray.initCoreEnv(assetPath, deviceId) + Log.i(AppConfig.TAG, "V2Ray core environment initialized successfully") + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to initialize V2Ray core environment", e) + initialized.set(false) + throw e + } + } else { + Log.d(AppConfig.TAG, "V2Ray core environment already initialized, skipping") + } + } + + + /** + * Get V2Ray core version. + * + * @return Version string of the V2Ray core + */ + fun getLibVersion(): String { + return try { + Libv2ray.checkVersionX() + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to check V2Ray version", e) + "Unknown" + } + } + + /** + * Measure outbound connection delay. + * + * @param config The configuration JSON string + * @param testUrl The URL to test against + * @return Delay in milliseconds, or -1 if test failed + */ + fun measureOutboundDelay(config: String, testUrl: String): Long { + return try { + Libv2ray.measureOutboundDelay(config, testUrl) + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to measure outbound delay", e) + -1L + } + } + + /** + * Create a new core controller instance. + * + * @param handler The callback handler for core events + * @return A new CoreController instance + */ + fun newCoreController(handler: CoreCallbackHandler): CoreController { + return try { + Libv2ray.newCoreController(handler) + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to create core controller", e) + throw e + } + } +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt index 43e90d4f..1cbe24f3 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt @@ -18,26 +18,23 @@ import com.v2ray.ang.service.V2RayProxyOnlyService import com.v2ray.ang.service.V2RayVpnService import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils -import go.Seq import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import libv2ray.CoreCallbackHandler import libv2ray.CoreController -import libv2ray.Libv2ray import java.lang.ref.SoftReference object V2RayServiceManager { - private val coreController: CoreController = Libv2ray.newCoreController(CoreCallback()) + private val coreController: CoreController = V2RayNativeManager.newCoreController(CoreCallback()) private val mMsgReceive = ReceiveMessageHandler() private var currentConfig: ProfileItem? = null var serviceControl: SoftReference? = null set(value) { field = value - Seq.setContext(value?.get()?.getService()?.applicationContext) - Libv2ray.initCoreEnv(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey()) + V2RayNativeManager.initCoreEnv(value?.get()?.getService()) } /** diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt index bb3237e7..68934f11 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt @@ -11,20 +11,18 @@ import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.serializable import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.PluginServiceManager -import com.v2ray.ang.handler.SpeedtestManager +import com.v2ray.ang.handler.SettingsManager +import com.v2ray.ang.handler.V2RayNativeManager import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.MessageUtil -import com.v2ray.ang.util.Utils -import go.Seq +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren -import kotlinx.coroutines.launch import kotlinx.coroutines.joinAll -import kotlinx.coroutines.CancellationException -import libv2ray.Libv2ray +import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicInteger class V2RayTestService : Service() { @@ -45,8 +43,7 @@ class V2RayTestService : Service() { */ override fun onCreate() { super.onCreate() - Seq.setContext(this) - Libv2ray.initCoreEnv(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey()) + V2RayNativeManager.initCoreEnv(this) } /** @@ -141,7 +138,7 @@ class V2RayTestService : Service() { if (!configResult.status) { return retFailure } - return SpeedtestManager.realPing(configResult.content) + return V2RayNativeManager.measureOutboundDelay(configResult.content, SettingsManager.getDelayTestUrl()) } } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt index 7ec4f76c..70b80ca3 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt @@ -5,7 +5,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.BuildConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityAboutBinding -import com.v2ray.ang.handler.SpeedtestManager +import com.v2ray.ang.handler.V2RayNativeManager import com.v2ray.ang.util.Utils class AboutActivity : BaseActivity() { @@ -42,7 +42,7 @@ class AboutActivity : BaseActivity() { Utils.openUri(this, AppConfig.APP_PRIVACY_POLICY) } - "v${BuildConfig.VERSION_NAME} (${SpeedtestManager.getLibVersion()})".also { + "v${BuildConfig.VERSION_NAME} (${V2RayNativeManager.getLibVersion()})".also { binding.tvVersion.text = it } BuildConfig.APPLICATION_ID.also { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt index fbd3739c..cae45425 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt @@ -13,8 +13,8 @@ import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastError import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.MmkvManager -import com.v2ray.ang.handler.SpeedtestManager import com.v2ray.ang.handler.UpdateCheckerManager +import com.v2ray.ang.handler.V2RayNativeManager import com.v2ray.ang.util.Utils import kotlinx.coroutines.launch @@ -36,7 +36,7 @@ class CheckUpdateActivity : BaseActivity() { } binding.checkPreRelease.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, false) - "v${BuildConfig.VERSION_NAME} (${SpeedtestManager.getLibVersion()})".also { + "v${BuildConfig.VERSION_NAME} (${V2RayNativeManager.getLibVersion()})".also { binding.tvVersion.text = it }