#android #retrofit2 #okhttp
Вопрос:
Я ищу вашу помощь / идеи, связанные с проблемой ниже:
Чего я хочу достичь:
- маршрутизация запросов через мобильную сеть на Android с использованием Retrofit2, OktHttp
Что я сделал:
- получен необходимый экземпляр сети с помощью ConnectivityManager
- установите SocketFactory для OkHttpClient, чтобы использовать тот, который находится в сетевом экземпляре
- укажите эти разрешения в AndroidManifest.xml.:
lt;uses-permission android:name="android.permission.INTERNET"/gt; lt;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/gt; lt;uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/gt; lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/gt; lt;uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/gt;
Пример кода:
val network: Network = connectivityManager.allNetworks.first { connectivityManager.getNetworkCapabilities(it)?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true } val okHttpClient = OkHttpClient.Builder() .socketFactory(network.socketFactory) .build()
Результат:
- возникает исключение: java.net.SocketException: Ошибка привязки сокета к сети 505: EACCES (отказано в разрешении)
Примечания / Отказ от ответственности:
- вышесказанное работает для Wi-Fi
- Я знаю, что это плохая практика: это не поведение приложения по умолчанию. Пользователи могут зарегистрироваться и отказаться в любое время. Есть конкретный случай использования, в котором это имеет смысл.
Если у вас есть какие-либо идеи о том, что я делаю неправильно, или почему это не работает для мобильных сетей, пожалуйста, дайте мне знать.
Ответ №1:
Мне удалось выяснить, почему я поступил неправильно.
В двух словах:
- вместо ConnectivityManager.getAllNetworks() используйте ConnectivityManager.requestNetwork(), указав сведения о сети и используйте полученную.
val cellularRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build() connectivityManager.requestNetwork(cellularRequest, object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { // use the network... } })
Длинная история:
При тестировании у меня был включен Wi-Fi и мобильные данные.
Из документов о ConnectivityManager.getAllNetworks():
- «Возвращает массив всей сети, отслеживаемой в настоящее время платформой».
- Я думаю, что ключевое слово здесь-это та часть, которую я выделил (интерфейс мобильной сети передачи данных не будет отслеживаться с самого начала, он не появится в списке. Когда я увидел тот, у которого есть возможности СОТОВОЙ связи, я подумал, что это тот, кого я ищу. Я ошибся. Это другой интерфейс. Я не заметил, что у него нет возможности доступа в Интернет.)
- по умолчанию он вернул 2 сетевых экземпляра, см. Ниже:
Сетевой идентификатор | Возможности |
---|---|
525 | Транспортировка: Возможности СОТОВОЙ связи: IMSamp;NOT_METEREDamp;НАДЕЖНЫЙamp;NOT_VPNamp;ПРОВЕРЕННЫЙamp;NOT_ROAMINGamp;ПЕРЕДНИЙ ПЛАНamp;NOT_CONGESTEDamp;NOT_SUSPENDED |
767 | Транспорт: Возможности WI-FI: NOT_METEREDamp;INTERNETamp;NOT_RESTRICTEDamp;TRUSTEDamp;NOT_VPNamp;VALIDATEDamp;NOT_ROAMINGamp;FOREGROUNDamp;NOT_CONGESTEDamp;NOT_SUSPENDED |
Обратите внимание, что СОТОВАЯ связь (идентификатор: 525) не имеет возможностей Интернета и имеет IMS (который, если я правильно понял из документов, относится к сети GSM). Когда я попытался привязать сокет к этой сети, это, очевидно, не удалось.
Затем я вызвал метод requestNetwork() и заметил, что получил новый (3-й) сетевой экземпляр:
Сетевой идентификатор | Возможности |
---|---|
770 | Транспортные средства: Возможности СОТОВОЙ связи: SUPLamp;INTERNETamp;NOT_RESTRICTEDamp;TRUSTEDamp;NOT_VPNamp;VALIDATEDamp;NOT_ROAMINGamp;FOREGROUNDamp;NOT_CONGESTEDamp;NOT_SUSPENDED |
Используя это, привязка работает без проблем.