Refactor speed notification and add outbound stats
Add OutboundTrafficStat DTO and a CoreServiceManager.queryAllOutboundTrafficStats() method to fetch/reset all outbound counters in one core call and parse Go-side payload. Remove ProfileItem.getAllOutboundTags() and simplify NotificationManager API (startSpeedNotification()/stopSpeedNotification()) to use aggregated stats via the new core query. Replace per-tag polling with a single aggregated update loop (updateSpeedNotificationOnce) to reduce IPC and CPU usage, and update CoreServiceManager call sites accordingly.
This commit is contained in:
1 parent
2a6edd98be
commit
237837e7be
4 files changed
+103
-56
No files matched your search
@@ -13,6 +13,7 @@ import androidx.core.content.ContextCompat
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.contracts.ServiceControl
|
||||
import com.v2ray.ang.dto.OutboundTrafficStat
|
||||
import com.v2ray.ang.dto.entities.ProfileItem
|
||||
import com.v2ray.ang.enums.EConfigType
|
||||
import com.v2ray.ang.extension.toast
|
||||
@@ -257,7 +258,7 @@ object CoreServiceManager {
|
||||
}
|
||||
|
||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
|
||||
NotificationManager.startSpeedNotification(currentConfig)
|
||||
NotificationManager.startSpeedNotification()
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Core started successfully")
|
||||
}
|
||||
|
||||
@@ -299,13 +300,32 @@ object CoreServiceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the statistics for a given tag and link.
|
||||
* @param tag The tag to query.
|
||||
* @param link The link to query.
|
||||
* @return The statistics value.
|
||||
* Queries and resets all outbound traffic counters in one core call.
|
||||
* Go side format: tag,direction,value;tag,direction,value;
|
||||
*/
|
||||
fun queryStats(tag: String, link: String): Long {
|
||||
return coreController.queryStats(tag, link)
|
||||
fun queryAllOutboundTrafficStats(): List<OutboundTrafficStat> {
|
||||
val payload = coreController.queryAllOutboundTrafficStats()
|
||||
|
||||
val result = ArrayList<OutboundTrafficStat>()
|
||||
|
||||
payload.split(';').forEach { entry ->
|
||||
if (entry.isBlank()) return@forEach
|
||||
|
||||
val parts = entry.split(',', limit = 3)
|
||||
if (parts.size != 3) return@forEach
|
||||
|
||||
val value = parts[2].toLongOrNull() ?: return@forEach
|
||||
|
||||
result.add(
|
||||
OutboundTrafficStat(
|
||||
tag = parts[0],
|
||||
direction = parts[1],
|
||||
value = value,
|
||||
)
|
||||
)
|
||||
}
|
||||
// LogUtil.d(AppConfig.TAG, "Queried outbound traffic stats: $result")
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,12 +508,12 @@ object CoreServiceManager {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_SCREEN_OFF -> {
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Screen off")
|
||||
NotificationManager.stopSpeedNotification(currentConfig)
|
||||
NotificationManager.stopSpeedNotification()
|
||||
}
|
||||
|
||||
Intent.ACTION_SCREEN_ON -> {
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Screen on")
|
||||
NotificationManager.startSpeedNotification(currentConfig)
|
||||
NotificationManager.startSpeedNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class OutboundTrafficStat(
|
||||
val tag: String,
|
||||
val direction: String,
|
||||
val value: Long,
|
||||
)
|
||||
@@ -76,10 +76,6 @@ data class ProfileItem(
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllOutboundTags(): MutableList<String> {
|
||||
return mutableListOf(AppConfig.TAG_PROXY, AppConfig.TAG_DIRECT, AppConfig.TAG_BLOCKED)
|
||||
}
|
||||
|
||||
fun getServerAddressAndPort(): String {
|
||||
if (server.isNullOrEmpty() && configType == EConfigType.CUSTOM) {
|
||||
return "${AppConfig.LOOPBACK}:${AppConfig.PORT_SOCKS}"
|
||||
|
||||
@@ -43,53 +43,15 @@ object NotificationManager {
|
||||
* Starts the speed notification.
|
||||
* @param currentConfig The current profile configuration.
|
||||
*/
|
||||
fun startSpeedNotification(currentConfig: ProfileItem?) {
|
||||
fun startSpeedNotification() {
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) return
|
||||
if (speedNotificationJob != null || CoreServiceManager.isRunning() == false) return
|
||||
|
||||
var lastZeroSpeed = false
|
||||
val outboundTags = currentConfig?.getAllOutboundTags()
|
||||
outboundTags?.remove(AppConfig.TAG_DIRECT)
|
||||
|
||||
speedNotificationJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
while (isActive) {
|
||||
val queryTime = System.currentTimeMillis()
|
||||
val sinceLastQueryIn = (queryTime - lastQueryTime)
|
||||
|
||||
// If the query interval is too short, skip this round to avoid excessive CPU usage
|
||||
if (sinceLastQueryIn < QUERY_INTERVAL_MS) {
|
||||
LogUtil.w(AppConfig.TAG, "Query interval too short: ${sinceLastQueryIn}ms, skipping")
|
||||
lastQueryTime = queryTime
|
||||
delay(QUERY_INTERVAL_MS)
|
||||
continue
|
||||
}
|
||||
val sinceLastQueryInSeconds = sinceLastQueryIn / 1000.0
|
||||
|
||||
var proxyTotal = 0L
|
||||
val text = StringBuilder()
|
||||
outboundTags?.forEach {
|
||||
val up = CoreServiceManager.queryStats(it, AppConfig.UPLINK)
|
||||
val down = CoreServiceManager.queryStats(it, AppConfig.DOWNLINK)
|
||||
if (up + down > 0) {
|
||||
appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
|
||||
proxyTotal += up + down
|
||||
}
|
||||
}
|
||||
val directUplink = CoreServiceManager.queryStats(AppConfig.TAG_DIRECT, AppConfig.UPLINK)
|
||||
val directDownlink = CoreServiceManager.queryStats(AppConfig.TAG_DIRECT, AppConfig.DOWNLINK)
|
||||
val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L
|
||||
if (!zeroSpeed || !lastZeroSpeed) {
|
||||
if (proxyTotal == 0L) {
|
||||
appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0)
|
||||
}
|
||||
appendSpeedString(
|
||||
text, AppConfig.TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
|
||||
directDownlink / sinceLastQueryInSeconds
|
||||
)
|
||||
updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
|
||||
}
|
||||
lastZeroSpeed = zeroSpeed
|
||||
lastQueryTime = queryTime
|
||||
lastZeroSpeed = updateSpeedNotificationOnce(lastZeroSpeed)
|
||||
delay(QUERY_INTERVAL_MS)
|
||||
}
|
||||
}
|
||||
@@ -168,13 +130,12 @@ object NotificationManager {
|
||||
|
||||
/**
|
||||
* Stops the speed notification.
|
||||
* @param currentConfig The current profile configuration.
|
||||
*/
|
||||
fun stopSpeedNotification(currentConfig: ProfileItem?) {
|
||||
fun stopSpeedNotification() {
|
||||
speedNotificationJob?.let {
|
||||
it.cancel()
|
||||
speedNotificationJob = null
|
||||
updateNotification(currentConfig?.remarks, 0, 0)
|
||||
updateNotification("", 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +208,69 @@ object NotificationManager {
|
||||
text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the speed notification once.
|
||||
* Queries traffic stats, separates proxy and direct, and updates the notification.
|
||||
* @param lastZeroSpeed The previous zero speed state.
|
||||
* @return The current zero speed state.
|
||||
*/
|
||||
private fun updateSpeedNotificationOnce(lastZeroSpeed: Boolean): Boolean {
|
||||
val queryTime = System.currentTimeMillis()
|
||||
val sinceLastQueryIn = (queryTime - lastQueryTime)
|
||||
|
||||
// If the query interval is too short, skip this round to avoid excessive CPU usage
|
||||
if (sinceLastQueryIn < QUERY_INTERVAL_MS) {
|
||||
LogUtil.w(AppConfig.TAG, "Query interval too short: ${sinceLastQueryIn}ms, skipping")
|
||||
lastQueryTime = queryTime
|
||||
return lastZeroSpeed
|
||||
}
|
||||
val sinceLastQueryInSeconds = sinceLastQueryIn / 1000.0
|
||||
|
||||
var proxyUplink = 0L
|
||||
var proxyDownlink = 0L
|
||||
var directUplink = 0L
|
||||
var directDownlink = 0L
|
||||
|
||||
CoreServiceManager.queryAllOutboundTrafficStats().forEach { stat ->
|
||||
when {
|
||||
stat.tag == AppConfig.TAG_DIRECT -> {
|
||||
when (stat.direction) {
|
||||
AppConfig.UPLINK -> directUplink += stat.value
|
||||
AppConfig.DOWNLINK -> directDownlink += stat.value
|
||||
}
|
||||
}
|
||||
|
||||
stat.tag.startsWith(AppConfig.TAG_PROXY) -> {
|
||||
when (stat.direction) {
|
||||
AppConfig.UPLINK -> proxyUplink += stat.value
|
||||
AppConfig.DOWNLINK -> proxyDownlink += stat.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val proxyTotal = proxyUplink + proxyDownlink
|
||||
val directTotal = directUplink + directDownlink
|
||||
val zeroSpeed = proxyTotal + directTotal == 0L
|
||||
if (!zeroSpeed || !lastZeroSpeed) {
|
||||
val text = StringBuilder()
|
||||
appendSpeedString(
|
||||
text, AppConfig.TAG_PROXY,
|
||||
proxyUplink / sinceLastQueryInSeconds,
|
||||
proxyDownlink / sinceLastQueryInSeconds
|
||||
)
|
||||
|
||||
appendSpeedString(
|
||||
text, AppConfig.TAG_DIRECT,
|
||||
directUplink / sinceLastQueryInSeconds,
|
||||
directDownlink / sinceLastQueryInSeconds
|
||||
)
|
||||
updateNotification(text.toString(), proxyTotal, directTotal)
|
||||
}
|
||||
lastQueryTime = queryTime
|
||||
return zeroSpeed
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service instance.
|
||||
* @return The service instance.
|
||||
|
||||
Reference in new issue
Block a user