Android 如何处理wifi和移动数据之间的网络变化?
我正在构建一个VoIP应用程序。在VoIP呼叫期间,当用户在WiFi和移动数据之间切换时,我在处理场景时遇到问题 在我的呼叫屏幕活动中,我注册了receiver,这有助于我获得有关网络更改场景的通知 这是我在OnReceive方法中用于检测网络变化的代码Android 如何处理wifi和移动数据之间的网络变化?,android,networking,android-wifi,mobile-data,android-connectivitymanager,Android,Networking,Android Wifi,Mobile Data,Android Connectivitymanager,我正在构建一个VoIP应用程序。在VoIP呼叫期间,当用户在WiFi和移动数据之间切换时,我在处理场景时遇到问题 在我的呼叫屏幕活动中,我注册了receiver,这有助于我获得有关网络更改场景的通知 这是我在OnReceive方法中用于检测网络变化的代码conn_name是保留以前连接名称的私有类级别变量 ConnectivityManager connectivity_mgr = ((ConnectivityManager) context.getSystemService(Context.C
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地址更新为WiFi IP地址,所以这里没有问题。但是仍然可以同时连接移动数据,但是getActiveNetworkInfo()告诉我,即使我很早就连接到移动数据,我也可以清楚地连接到WiFi
所以问题是当用户关闭WiFi按钮时,移动数据仍然连接,但我仍然收到WiFi关闭的通知。
它表示即使我的手机仍然连接到移动数据,网络也已断开
但过了一秒钟,我收到一个通知,移动数据已连接。
但一旦我收到断开的网络,我就关闭了我的VoIP呼叫。
因此,当我收到关闭WiFi的通知时,我如何确保移动数据是否仍然连接
我尝试了getActiveNetworkInfo(),但当我收到关闭WiFi的通知时,它恰好为空
我关注了以下链接:
使用上面的链接,我可以检测到当用户连接到mobiledata时,移动数据按钮已启用。它为我提供了真实信息。 但当这种特殊情况发生时,问题就出现了 现在,当wifi被禁用时,我会收到通知,但它显示移动数据被禁用,即使我的移动数据已启用。我无法处理这种情况,因为当我收到断开连接的通知时,我会断开呼叫。您可以使用以下API:尤其是在您感兴趣的用例中: 我的设备在大约半秒钟内连接到LTE 这意味着,当WIFI断开时,您无法事先知道设备最终是否会连接到LTE。因此,您可以采用以下方法:在处理程序上发布一个动作以在一秒钟内发生,并在此动作中取消调用。如果连接很快出现-不计划以前发布的操作。如果您最终处于
Runnable
code中,则连接没有快速建立,这意味着您应该结束呼叫
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);
}
}
这种方法的缺点是,可以从API 24开始使用。在这两种情况下都不存在替代方案。相反,您可以使用API 21提供的功能。您可以使用
广播接收器并注册网络状态更改操作
和WIFI状态更改操作
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();
}
更新别忘了注销ReceiverBroadcastReceiver
onDestroy
@Override
protected void onDestroy() {
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
我的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)
}
}
使用定时器/一次性允许在切换连接类型之间进行延迟。让我尝试使用registerDefaultNetworkCallback,这是您之前不知道的。或者,您可以注册两个单独的回调,一个用于TRANSPORT_WIFI,另一个用于TRANSPORT_CELLULAR。这样,如果两个网络都可用,您就可以引用它们。大多数原始设备制造商总是在后台保持手机网络的活跃。如果没有,您可以使用CM#requestNetwork请求蜂窝网络。当wifi断开时,您可以保持1-2秒的暂停。然后在1-2秒后,尝试重新检查移动数据是否可用。@Rahulrr2602如果没有任何解决方案,则是。我计划使用此wat。当移动数据连接时,在接收wifi的网络通知时发生此情况down@JeevaisConnected
使用联机实用程序函数在onCreate方法内初始化标志,和isConnected
标志将在旧状态和当前状态之间进行判断onReceive
操作。@Jeeva您可以使用具有延迟的处理程序在Wifi和移动数据之间切换结束通话内容,如上所述。
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)
}
}