2 Commits

Author SHA1 Message Date
zarazaex69 a04c53a045 fix: real ping IPC stalls by batching and throttling UI updates 2026-04-21 04:27:37 +03:00
zarazaex69 278095015b feat: remove apk 2026-04-21 03:06:16 +03:00
5 changed files with 78 additions and 6 deletions
@@ -164,6 +164,7 @@ object AppConfig {
const val MSG_MEASURE_CONFIG_CANCEL = 72 const val MSG_MEASURE_CONFIG_CANCEL = 72
const val MSG_MEASURE_CONFIG_NOTIFY = 73 const val MSG_MEASURE_CONFIG_NOTIFY = 73
const val MSG_MEASURE_CONFIG_FINISH = 74 const val MSG_MEASURE_CONFIG_FINISH = 74
const val MSG_MEASURE_CONFIG_BATCH = 75
/** Notification channel IDs and names. */ /** Notification channel IDs and names. */
const val RAY_NG_CHANNEL_ID = "RAY_NG_M_CH_ID" const val RAY_NG_CHANNEL_ID = "RAY_NG_M_CH_ID"
@@ -0,0 +1,14 @@
package xyz.zarazaex.olc.dto
import java.io.Serializable
data class PingResultItem(
val guid: String,
val delay: Long
) : Serializable
data class PingProgressUpdate(
val results: ArrayList<PingResultItem>,
val finished: Int,
val total: Int
) : Serializable
@@ -6,8 +6,12 @@ import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import xyz.zarazaex.olc.AppConfig import xyz.zarazaex.olc.AppConfig
import xyz.zarazaex.olc.dto.PingProgressUpdate
import xyz.zarazaex.olc.dto.PingResultItem
import xyz.zarazaex.olc.handler.SettingsManager import xyz.zarazaex.olc.handler.SettingsManager
import xyz.zarazaex.olc.handler.V2RayNativeManager import xyz.zarazaex.olc.handler.V2RayNativeManager
import xyz.zarazaex.olc.handler.V2rayConfigManager import xyz.zarazaex.olc.handler.V2rayConfigManager
@@ -29,12 +33,26 @@ class RealPingWorkerService(
private val totalCount = AtomicInteger(guids.size) private val totalCount = AtomicInteger(guids.size)
private val finishedCount = AtomicInteger(0) private val finishedCount = AtomicInteger(0)
private val pendingResults = ArrayList<PingResultItem>()
private val pendingLock = Any()
private val delayTestUrl = SettingsManager.getDelayTestUrl() private val delayTestUrl = SettingsManager.getDelayTestUrl()
companion object {
private const val RESULT_BATCH_SIZE = 32
private const val FLUSH_INTERVAL_MS = 1000L
}
data class PingItem(val guid: String, val config: String) data class PingItem(val guid: String, val config: String)
fun start() { fun start() {
scope.launch(Dispatchers.IO) {
while (isActive) {
delay(FLUSH_INTERVAL_MS)
flushPendingResults()
}
}
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
try { try {
// Prepare configurations for batch test and shuffle for better async feel // Prepare configurations for batch test and shuffle for better async feel
@@ -67,8 +85,10 @@ class RealPingWorkerService(
) )
} }
flushPendingResults()
onFinish("0") onFinish("0")
} catch (e: Exception) { } catch (e: Exception) {
flushPendingResults()
onFinish("-1") onFinish("-1")
} finally { } finally {
cancel() cancel()
@@ -78,14 +98,42 @@ class RealPingWorkerService(
private fun reportResult(guid: String, delay: Long) { private fun reportResult(guid: String, delay: Long) {
val finished = finishedCount.incrementAndGet() val finished = finishedCount.incrementAndGet()
val total = guids.size var readyBatch: PingProgressUpdate? = null
synchronized(pendingLock) {
pendingResults.add(PingResultItem(guid, delay))
if (pendingResults.size >= RESULT_BATCH_SIZE || finished >= totalCount.get()) {
readyBatch = createProgressUpdateLocked(finished)
pendingResults.clear()
}
}
readyBatch?.let(::sendBatchUpdate)
}
// Notify UI about the individual result private fun flushPendingResults() {
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, delay)) val finished = finishedCount.get()
val update =
synchronized(pendingLock) {
if (pendingResults.isEmpty()) {
null
} else {
createProgressUpdateLocked(finished).also { pendingResults.clear() }
}
}
update?.let(::sendBatchUpdate)
}
// Notify UI about progress private fun createProgressUpdateLocked(finished: Int): PingProgressUpdate {
val left = total - finished return PingProgressUpdate(
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / $total") results = ArrayList(pendingResults),
finished = finished,
total = totalCount.get()
)
}
private fun sendBatchUpdate(update: PingProgressUpdate) {
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_BATCH, update)
val left = (update.total - update.finished).coerceAtLeast(0)
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / ${update.total}")
} }
fun cancel() { fun cancel() {
@@ -15,6 +15,7 @@ import xyz.zarazaex.olc.AngApplication
import xyz.zarazaex.olc.AppConfig import xyz.zarazaex.olc.AppConfig
import xyz.zarazaex.olc.R import xyz.zarazaex.olc.R
import xyz.zarazaex.olc.dto.GroupMapItem import xyz.zarazaex.olc.dto.GroupMapItem
import xyz.zarazaex.olc.dto.PingProgressUpdate
import xyz.zarazaex.olc.dto.ServersCache import xyz.zarazaex.olc.dto.ServersCache
import xyz.zarazaex.olc.dto.SubscriptionCache import xyz.zarazaex.olc.dto.SubscriptionCache
import xyz.zarazaex.olc.dto.SubscriptionUpdateResult import xyz.zarazaex.olc.dto.SubscriptionUpdateResult
@@ -628,6 +629,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
updateListAction.value = getPosition(resultPair.first) updateListAction.value = getPosition(resultPair.first)
} }
AppConfig.MSG_MEASURE_CONFIG_BATCH -> {
val update = intent.serializable<PingProgressUpdate>("content") ?: return
update.results.forEach { result ->
MmkvManager.encodeServerTestDelayMillis(result.guid, result.delay)
}
updateListAction.value = -1
}
AppConfig.MSG_MEASURE_CONFIG_NOTIFY -> { AppConfig.MSG_MEASURE_CONFIG_NOTIFY -> {
val content = intent.getStringExtra("content") val content = intent.getStringExtra("content")
updateTestResultAction.value = updateTestResultAction.value =
BIN
View File
Binary file not shown.