Как обрабатывать изменение сети между 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 отключен, я получаю уведомление, но оно показывает, что мобильные данные отключены, даже если мои мобильные данные включены. Я не могу справиться с этой ситуацией, так как я отключаю свои звонки, когда я получаю свое отключенное уведомление.
Ответы
Ответ 1
Вы можете использовать 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 wifi
// nevertheless, for simplicity let assume that 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 примерно за полсекунды.
![enter image description here]()
Это означает, что вы не можете заранее знать, будет ли устройство в конечном итоге подключаться к 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.
Ответ 2
Вы можете использовать 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();
}
Обновить Не забудьте отменить регистрациюReceiver BroadcastReceiver
onDestroy
@Override
protected void onDestroy() {
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
Ответ 3
Ответ, который вы ищете, это BroadcastReceiver
. Проверьте ссылку ниже.
BroadcastReceiver при изменении состояния сети Wi-Fi или 3g
Надеюсь, он поможет вам решить вашу проблему, и если да, рассмотрите вопрос о закрытии вопроса.
Ответ 4
Моя реализация с 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)
}
}
Использование таймера/одноразового использования позволяет задерживать переключение между типами подключения.