Вопрос или проблема
Я исследую проблему, при которой передача данных по Wi-Fi между моим приложением для Android и GoPro несовместима в различных условиях мобильной сети. Сначала я заметил, что GoPro Quik (официальное приложение GoPro) мог эффективно передавать данные даже в сетях 2G, в то время как моё приложение не могло. После некоторого изучения их исходного кода я понял, что Quik переопределяет read_buffer_size
в OkHttp. Как только я внес такую же корректировку в своём приложении, оно стало хорошо передавать данные в 2G, что улучшило производительность в этих случаях. Однако это решение не было идеальным. Иногда передача данных замедлялась даже на 4G, и я заметил, что GoPro Quik также испытывал медленные скорости передачи в этих случаях. Интересно, что скорости значительно увеличивались, как только мобильное соединение отключалось, что может указывать на то, что между мобильной и Wi-Fi сетями существует взаимодействие, которое влияет на производительность. Я ищу способы логирования размеров буферов, чтобы продолжить исследование, и рассматриваю, как Android или основной ядро Linux могут влиять на эти поведения.
Буду признателен за любые предложения или теории о том, как именно качество мобильной сети может оказать влияние.
Вот мой код для создания клиента OkHttp с пользовательской фабрикой сокетов, которая переопределяет read_buffer_size
:
class CustomBufferSocketFactory(
private val sendBufferSizeBytes: Int,
private val receiveBufferSizeBytes: Int,
private val tcpNoDelayEnabled: Boolean = false,
private val socketFactory: SocketFactory = SocketFactory.getDefault()
) : SocketFactory() {
override fun createSocket(): Socket {
return configureSocket(socketFactory.createSocket())
}
override fun createSocket(host: String, port: Int): Socket {
return configureSocket(socketFactory.createSocket(host, port))
}
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
return configureSocket(socketFactory.createSocket(host, port, localHost, localPort))
}
override fun createSocket(address: InetAddress, port: Int): Socket {
return configureSocket(socketFactory.createSocket(address, port))
}
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
return configureSocket(socketFactory.createSocket(address, port, localAddress, localPort))
}
private fun configureSocket(socket: Socket): Socket {
socket.sendBufferSize = sendBufferSizeBytes
socket.receiveBufferSize = receiveBufferSizeBytes
socket.tcpNoDelay = tcpNoDelayEnabled
return socket
}
}
suspend fun connectToLocalNetwork(
context: Context,
ssid: String,
password: String
): OkHttpClient? {
return withTimeoutOrNull(15000L) { // Таймаут через 15 секунд
suspendCancellableCoroutine { continuation ->
var isResumed = false
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// Используйте WifiNetworkSpecifier для создания запроса на желаемую Wi-Fi сеть
val wifiSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build()
// Создание запроса на сеть
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.removeTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.setNetworkSpecifier(wifiSpecifier)
.build()
// Определите обратный вызов сети
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
if (!isResumed && !continuation.isCancelled) {
isResumed = true
// Привязка сетевого трафика вашего приложения к конкретной сети
connectivityManager.bindProcessToNetwork(network)
Logger.getLogger(OkHttpClient::class.java.name).setLevel(Level.FINE)
// Используйте OkHttpClientFactory для загрузки больших файлов
val client =
OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.socketFactory(CustomBufferSocketFactory(
524288, 524288, true, network.socketFactory))
.build()
continuation.resume(client)
}
}
override fun onUnavailable() {
if (!isResumed && !continuation.isCancelled) {
isResumed = true
continuation.resume(null)
}
}
override fun onLost(network: Network) {
if (!isResumed && !continuation.isCancelled) {
isResumed = true
continuation.resume(null)
}
}
}
connectivityManager.requestNetwork(networkRequest, networkCallback)
}
}
}
Ответ или решение
Кажется, что у вас возникла проблематика, связанная с передачей данных между вашим приложением и GoPro на различных условиях сетевого соединения, особенно с Wi-Fi под влиянием сотовой сети. Давайте подробнее разберем возможные причины и предложим пути решения.
Причины
-
Влияние сотовой сети: Когда ваше устройство подключено к Wi-Fi и одновременно имеет доступ к сотовой сети, Android может использовать как Wi-Fi, так и сотовую связь для передачи данных. Это может приводить к эффекту, когда Wi-Fi соединение становится нестабильным из-за того, что система пытается использовать сотовую связь в качестве резервного канала. В результате осыщающая письмогерия может повлиять на затраты по вашим буферам и скорость передачи.
-
Настройки буферов сокета: Вы правильно определили, что изменение параметров
read_buffer_size
иsend_buffer_size
может повлиять на производительность. Однако, хотя вы заметили улучшение на 2G, это не исключает возможности, что оптимальные параметры могут различаться в зависимости от условий соединения (Wi-Fi, 3G, 4G и т.д.), что требует гибкой настройки. - Текущая реализация Android: Android и его инфраструктура могут по-разному обрабатывать сетевые соединения. Например, некоторые версии Android могут использовать другие механизмы для управления сетевыми соединениями, что влияет на приоритеты и условия использования Wi-Fi или сотовой сети.
Решения
-
Логирование состояния: Введите детализированное логирование событий, касающихся вашего сетевого соединения, включая значения
sendBufferSize
иreceiveBufferSize
перед и после соединения. Это позволит вам увидеть, как они изменяются в зависимости от условий сети. -
Динамическое изменение буферов: Рассмотрите возможность динамического изменения параметров буферов на основе текущего состояния сети (например, переключение между 2G, 3G, 4G и Wi-Fi). Это может потребовать наблюдения за условиями сети и соответствующей настройки значений серверных буферов.
-
Ограничение использования сотовой сети: Приоритет выбора именно Wi-Fi соединения может помочь в избежании возможных конфликтов. Для этого можно изменить настройки в вашем
NetworkRequest
, чтобы исключить любую возможность подключения к сотовой сети (например, детальнее настраиватьremoveTransportType
). - Тестирование на различных устройствах и версиях Android: Разные устройства и различные версии прошивок Android могут иметь разные поведения в отношении сетевых соединений. Тестирование может выявить специфические проблемы и особенности на различных устройствах.
Пример логирования
Добавьте следующий код в свою реализацию для логирования буферов, чтобы вы могли следить за их изменением:
private fun configureSocket(socket: Socket): Socket {
Log.d("SocketConfig", "Setting send buffer size to $sendBufferSizeBytes")
Log.d("SocketConfig", "Setting receive buffer size to $receiveBufferSizeBytes")
socket.sendBufferSize = sendBufferSizeBytes
socket.receiveBufferSize = receiveBufferSizeBytes
socket.tcpNoDelay = tcpNoDelayEnabled
return socket
}
Заключение
Обратите внимание на вышеуказанные аспекты, и вы сможете лучше разобраться в проблемах передачи данных между вашим Android-приложением и GoPro на различных условиях сетевого соединения. Удачи вам в разработке!