Как справиться с изменением сети между Wi-Fi и мобильными данными?
Я создаю приложение VoIP. Во время вызова VoIP, когда пользователь переключается между Wi-Fi и мобильными данными, у меня есть проблема с обработкой сценария.
В своей деятельности на экране вызова я зарегистрировался для получателя, который помогает мне получать уведомления о сценариях изменения сети.
Это код, который я использую для обнаружения изменений в сетях в методе onRecieve.
conn_name - это закрытая переменная уровня класса, содержащая предыдущее имя соединения.
ConnectivityManager connectivity_mgr = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
NetworkInfo net_info = connectivity_mgr.getActiveNetworkInfo();
if (net_info != null && net_info.isConnectedOrConnecting() && !conn_name.equalsIgnoreCase("")) {
new_con = net_info.getExtraInfo();
if (new_con != null && !new_con.equalsIgnoreCase(conn_name))
network_changed = true;
conn_name = (new_con == null) ? "" : new_con;
connectionStatus ="connected";
} else {
if (net_info != null && conn_name.equalsIgnoreCase("")){
conn_name = net_info.getExtraInfo();
connectionStatus ="connected";
network_changed = true;
}else if(!new_con.equals(conn_name)) {
conn_name = "";
connectionStatus ="disconnected";
network_changed = true;
}
}
Таким образом, используя вышеописанный метод, я смог обнаружить изменения в сети. Но одна особенность происходит, когда я подключен к WiFi.
Когда мое приложение запускается изначально, оно подключается к мобильным данным. Когда пользователь входит в свою известную зону WiFi, он подключается к своей известной сети WiFi. Так как WiFi всегда выбирается в качестве маршрута по умолчанию, Android переключается на WiFi, и я получаю сетевое уведомление о том, что WiFi включен. Поэтому я обновляю IP-адрес своих приложений до IP-адреса WiFi. Так что никаких проблем здесь нет. Но мобильные данные все еще подключены в то же время, но getActiveNetworkInfo() говорит мне, что я четко подключен к WiFi, даже если раньше был подключен к мобильным данным.
так что проблема в том, что когда пользователь отключает кнопку WiFi, и мобильные данные все еще подключены, но я по-прежнему получаю уведомление об отключении WiFi. Это указывает на то, что сеть отключена, даже если мой телефон все еще подключен к мобильным данным. Но через секунду я получаю уведомление, что мобильные данные подключены. Но как только я получаю отключенную сеть, я закрыл свой вызов VoIP. Поэтому, когда я получаю уведомление о выключенном WiFi, как я могу убедиться, что мобильные данные все еще подключены. Я пробовал getActiveNetworkInfo(), но когда я получаю уведомление о том, что WiFi выключен, он оказывается пустым. Поэтому, пожалуйста, помогите мне решить эту проблему.
Я перешел по этой ссылке.
Вызов Android API для определения настроек пользователя "Данные включены"
Как определить, включена ли функция "Данные мобильной сети" (даже если она подключена через WiFi)?
Используя вышеупомянутую ссылку, я могу обнаружить, что кнопка мобильных данных была включена, когда пользователь подключился к mobiledata.it дает мне истину. Но проблема возникает, когда происходит именно этот случай.
"Теперь, когда Wi-Fi отключен, я получаю уведомление, но оно показывает, что мобильные данные отключены, даже когда мои мобильные данные включены. Я не могу справиться с этой ситуацией, так как я отключаю свои вызовы, когда я получаю свое отключенное уведомление"
5 ответов
Вы можете использовать API ConnectivityManager
: особенно в вашем случае вы заинтересованы в registerDefaultNetworkCallback()
:
public class TestActivity extends AppCompatActivity {
private ConnectivityManager manager;
private final ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
// this ternary operation is not quite true, because non-metered doesn't yet mean, that it's wifi
// nevertheless, for simplicity let's assume that's true
Log.i("vvv", "connected to " + (manager.isActiveNetworkMetered() ? "LTE" : "WIFI"));
}
@Override
public void onLost(Network network) {
super.onLost(network);
Log.i("vvv", "losing active connection");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
manager.registerDefaultNetworkCallback(networkCallback);
}
@Override
protected void onDestroy() {
super.onDestroy();
manager.unregisterNetworkCallback(networkCallback);
}
}
Мое устройство подключается к LTE примерно за полсекунды.
Это означает, что вы не можете заранее знать, будет ли устройство в конечном итоге подключаться к LTE или нет в то время, когда WIFI отключится. Таким образом, вы можете использовать следующий подход: опубликовать действие в обработчике, которое произойдет за секунду, и в рамках этого действия отменить вызов. Если соединение появится в ближайшее время - отмените ранее опубликованное действие. Если вы в конечном итоге в Runnable
код, после чего соединение не было установлено быстро, что означает, что вы должны завершить вызов.
public class TestActivity extends AppCompatActivity {
private ConnectivityManager manager;
private final Handler handler = new Handler();
private final ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.i("vvv", "connected to " + (manager.isActiveNetworkMetered() ? "LTE" : "WIFI"));
// we've got a connection, remove callbacks (if we have posted any)
handler.removeCallbacks(endCall);
}
@Override
public void onLost(Network network) {
super.onLost(network);
Log.i("vvv", "losing active connection");
// Schedule an event to take place in a second
handler.postDelayed(endCall, 1000);
}
};
private final Runnable endCall = new Runnable() {
@Override
public void run() {
// if execution has reached here - feel free to cancel the call
// because no connection was established in a second
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
manager.registerDefaultNetworkCallback(networkCallback);
}
@Override
protected void onDestroy() {
super.onDestroy();
manager.unregisterNetworkCallback(networkCallback);
handler.removeCallbacks(endCall);
}
}
Недостатком подхода является то, что registerDefaultNetworkCallback()
доступно начиная с API 24. Альтернативы в ConnectivityManagerCompat
или. Вместо этого вы можете использовать registerNetworkCallback()
который доступен из API 21.
Моя реализация с RxJava
class ConnectivityMonitor : ConnectivityManager.NetworkCallback() {
var networkTimeout: Disposable? = null
override fun onAvailable(network: Network?) {
super.onAvailable(network)
Timber.d("Network available")
networkTimeout?.dispose()
}
override fun onLosing(network: Network?, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
Timber.d("onLosing")
}
override fun onLost(network: Network?) {
super.onLost(network)
Timber.d("onLost")
networkTimeout = Single.timer(5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { _ -> Timber.d("Network lost") }
}
override fun onUnavailable() {
super.onUnavailable()
Timber.d("Network unavailable")
}
}
Настройка слушателя:
private fun setupListeners() {
// connection listener
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.registerDefaultNetworkCallback(connectivityMonitor)
} else {
val builder = NetworkRequest.Builder()
connectivityManager.registerNetworkCallback(builder.build(), connectivityMonitor)
}
}
Использование таймера / одноразового использования позволяет задерживать переключение между типами подключения.
Ты можешь использовать BroadcastReceiver
и зарегистрируйтесь NETWORK_STATE_CHANGED_ACTION
& WIFI_STATE_CHANGED_ACTION
,
private boolean isConnected;
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || intent.getAction() == null)
return;
switch (intent.getAction()){
case WifiManager.NETWORK_STATE_CHANGED_ACTION :
case WifiManager.WIFI_STATE_CHANGED_ACTION :
if (!isConnected && isOnline(BaseActivity.this)) {
isConnected = true;
// do stuff when connected
Log.i("Network status: ","Connected");
}else{
isConnected = isOnline(BaseActivity.this);
Log.i("Network status: ","Disconnected");
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
isConnected = isOnline(this);
final IntentFilter filters = new IntentFilter();
filters.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filters.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
registerReceiver(broadcastReceiver, filters);
}
public static boolean isOnline(Context ctx) {
ConnectivityManager cm = (ConnectivityManager) ctx
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm != null
? cm.getActiveNetworkInfo()
: null;
return netInfo != null && netInfo.isConnectedOrConnecting();
}
Обновление Не забудьте отменить регистрацию приемника BroadcastReceiver
OnDestroy
@Override
protected void onDestroy() {
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
спасибо за ваш вклад @azizbiken, я реализовал то же самое вProducerScope
в сочинении
fun networkCallback(producer: ProducerScope<ConnectionState>, context: Context, callback: (ConnectionState) -> Unit): ConnectivityManager.NetworkCallback {
return object : ConnectivityManager.NetworkCallback() {
var unAvailableJob: Job? = null
override fun onAvailable(network: Network) {
//cancel the coroutine once you get network instead of updating no network status
unAvailableJob?.cancel()
callback(ConnectionState.Available)
}
override fun onLost(network: Network) {
unAvailableJob = producer.launch {
delay(1000)
callback(ConnectionState.Unavailable)
}
}
}
}
а вот код вызова
val callback = networkCallback(this,context = applicationContext, callback = { connectionState -> trySend(connectionState) })
Ответ, который вы ищете, BroadcastReceiver
, Проверьте ссылку ниже.
BroadcastReceiver при изменении состояния сети Wi-Fi или 3G
Надеюсь, что это поможет вам решить вашу проблему, и если это так, рассмотрите возможность закрытия вопроса.