How to Efficiently Test V2Ray/Xray Configs in Android App Using libv2ray?
01:55 01 Dec 2025

I'm developing an Android VPN app that fetches and tests V2Ray/Xray proxy configurations. I need to find working servers quickly, but my current implementation using libv2ray.measureOutboundDelay() is too slow and sometimes crashes.

Current Implementation

Test Flow:

  1. Download configs from multiple sources (~100-150 configs)
  2. Parse and validate them
  3. Test each config using Libv2ray.measureOutboundDelay()
  4. Return working configs sorted by ping

Issues:

  • Slow: Testing 100 configs takes 5+ minutes
  • Crashes: OutOfMemoryError when testing many configs in parallel
  • Timeout: Some configs take 10+ seconds to test
  • Resource intensive: Multiple Xray instances running simultaneously

What I've Tried

Approach 1: Parallel Testing with Coroutines

class RealPingTester(private val context: Context) {
    
    companion object {
        private const val TEST_URL = "http://www.gstatic.com/generate_204"
        private const val MAX_PARALLEL_TESTS = 10
    }
    
    suspend fun testConfigsRealPing(
        configs: List,
        onProgress: (tested: Int, total: Int) -> Unit
    ): List = coroutineScope {
        
        val semaphore = Semaphore(MAX_PARALLEL_TESTS)
        
        configs.map { config ->
            async(Dispatchers.IO) {
                semaphore.withPermit {
                    val delay = realPing(config)
                    
                    if (delay > 0 && delay < 8000) {
                        config.copy(
                            ping = delay.toInt(),
                            isWorking = true
                        )
                    } else {
                        null
                    }
                }
            }
        }.awaitAll().filterNotNull()
    }
    
    private suspend fun realPing(profile: ProfileItem): Long {
        return withContext(Dispatchers.IO) {
            try {
                // Generate V2Ray JSON config
                val config = ProfileItemManager.generateProfileItem(profile)
                
                // Test using libv2ray
                val delay = withTimeoutOrNull(8000L) {
                    Libv2ray.measureOutboundDelay(config, TEST_URL)
                } ?: -1L
                
                delay
            } catch (e: Exception) {
                -1L
            }
        }
    }
}‍‍‍‍‍

Result: Still too slow (~5 minutes for 100 configs), occasional crashes

Data Model

data class ProfileItem(
    val configType: ConfigType, // VMESS, VLESS, TROJAN, SHADOWSOCKS
    val server: String,
    val serverPort: String,
    val password: String,       // UUID for VMess/VLESS, password for others
    
    // Transport settings
    val network: String? = null,     // tcp, ws, grpc, etc.
    val security: String? = null,    // tls, reality, none
    val path: String? = null,
    val host: String? = null,
    
    // Test results
    var ping: Int = 0,
    var isWorking: Boolean = false,
    var lastTested: Long = 0
)

Questions

What's the best strategy to test 100+ proxy configs efficiently?

Should I use TCP ping first, then real test? How many configs should I test in parallel without crashes? What's the optimal timeout for measureOutboundDelay()? How does v2rayNG test configs so quickly?

They seem to test dozens of configs in seconds Is there a better libv2ray API I'm missing? Do they use a different approach? Are there alternatives to measureOutboundDelay()?

Can I test multiple configs with a single Xray instance? Should I implement native connection testing instead? Is there a batch testing API in libv2ray? Memory management:

How to prevent OutOfMemoryError when testing many configs? Should I test in smaller batches? How to properly cleanup Xray instances?

android kotlin proxy v2ray