问题
I am building a VoIP app.During a VoIP call when user switches between WiFi to mobile data i have a problem handling the scenario.
In my call screen activity I have registered for receiver which helps me get notified about the network change scenarios.
This is the code which I am using for detecting change in networks in the onRecieve Method. conn_name
is private class level variable holding previous connection 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;
}
}
So using above method I able detect network changes. But one peculiar thing happens when I am connected with WiFi. When my app starts initially it is connected with mobile data.when user enters into his known WiFi area,he gets connected to his known WiFi. Since WiFi is always chosen as default route,android switches to WiFi and I receive the network notification that WiFi has been turned on.
So I update my apps IP address to WiFi IP address, so no issues here. But still mobile data is still connected at the same time but getActiveNetworkInfo() tells me that I'm connected with WiFi clearly even if I was early connected to mobile data.
So the problem is when user switches off the WiFi button,and mobile data is still connected but I still receive the notification for WiFi turn off. It indicates me that network is disconnected even when my phone is still connected to mobile data.
But after a second I receive a notification that mobile data is connected. But once I receive the network disconnected, I have closed my VoIP call. So when I receive a notification for WiFi switched off how can I make sure whether mobile data is still connected.
I tried getActiveNetworkInfo() but it happens to be null when I receive notification for WiFi turned off.
I have followed this links:
Android API call to determine user setting "Data Enabled"
How to tell if 'Mobile Network Data' is enabled or disabled (even when connected by WiFi)?
Using the above link I am able detect that mobile data button has been enabled when user has connected with mobiledata.it gives me true. But the problem happens when this particular case happens.
Now when wifi is disabled I get notification but it shows that mobile data is disabled even when my mobile data is enabled. I am not able to handle this situation as I disconnect my calls when I receive my disconnected notification.
回答1:
You can utilize APIs of ConnectivityManager: particularly in your use case you are interested in 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);
}
}
My device connects to LTE in about half second.
This means, that you cannot know beforehand, whether device will eventually connect to LTE or no at the time, when WIFI gets disconnected. Thus, you can adopt following approach: post an action on a handler to happen in a second and within this action cancel the call. If connection appears anytime soon - unschedule previously posted action. If you end up being in Runnable
code, then connection wasn't established quickly, which means, that you should end the call.
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);
}
}
The downside of the approach is, that registerDefaultNetworkCallback() is available starting from API 24. There does not exist an alternative in ConnectivityManagerCompat either. Instead, you can use registerNetworkCallback() which is available from API 21.
回答2:
You can use BroadcastReceiver
and register 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();
}
Update Don't forget to unregisterReceiver BroadcastReceiver
onDestroy
@Override
protected void onDestroy() {
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
回答3:
My implementation with 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")
}
}
Listener setup:
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)
}
}
The use of the timer / disposable allows for delays between switching connection types.
回答4:
The answer you're looking for is a BroadcastReceiver
. Check the link below.
BroadcastReceiver when wifi or 3g network state changed
Hope it helps you to solve your issue and if so, please consider closing your question.
来源:https://stackoverflow.com/questions/48080336/how-to-handle-network-change-between-wifi-and-mobile-data