VoIPアプリを構築しています。ユーザーがWiFiからモバイルデータに切り替えるときにVoIP通話中に、シナリオの処理に問題があります。
通話画面アクティビティでは、ネットワーク変更シナリオについて通知を受けるのに役立つレシーバーに登録しました。
これは、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に接続しているとき、1つの奇妙なことが起こります。アプリが最初に起動すると、モバイルデータに接続されます。ユーザーが既知のWiFiエリアに入ると、既知のWiFiに接続されます。 WiFiは常にデフォルトルートとして選択されるため、AndroidはWiFiに切り替わり、WiFiがオンになったというネットワーク通知を受け取ります。
そこで、アプリのIPアドレスをWiFi IPアドレスに更新するので、ここで問題は発生しません。ただし、モバイルデータはまだ同時に接続されていますが、getActiveNetworkInfo()は、たとえ早く接続したとしても、WiFiに明確に接続していることを示しますモバイルデータ。
そのため、ユーザーがWiFiボタンをオフにし、モバイルデータはまだ接続されているのに、WiFiがオフになっているという通知を受け取ったときに問題が発生します。携帯電話がまだモバイルデータに接続されていても、ネットワークが切断されていることを示しています。
しかし、1秒後にモバイルデータが接続されたという通知を受け取ります。しかし、切断されたネットワークを受信したら、VoIP通話を終了しました。したがって、WiFiがオフになったという通知を受け取ったときに、モバイルデータがまだ接続されているかどうかを確認する方法を教えてください。
GetActiveNetworkInfo()を試しましたが、WiFiがオフになったという通知を受け取ったときにnullになることがあります。
私はこのリンクをたどっています:
ユーザー設定「データ有効」を決定するためのAndroid API呼び出し
「WiFiで接続している場合でも」「モバイルネットワークデータ」が有効か無効かを確認する方法
上記のリンクを使用すると、ユーザーがmobiledata.itに接続したときにモバイルデータボタンが有効になったことを検出できます。しかし、この特定のケースが発生すると、問題が発生します。
これで、wifiが無効になったときに通知を受け取りますが、モバイルデータが有効になっていてもモバイルデータが無効になっていることがわかります。切断通知を受け取ったときにコールを切断すると、この状況に対処できません。
ConnectivityManager
:のAPIを利用できます。特に、ユースケースに興味がある場合は 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に約0.5秒で接続します。
つまり、WIFIが切断されたときにデバイスが最終的にLTEまたはnoに接続するかどうかを事前に知ることはできません。したがって、次のアプローチを採用できます。すぐに接続が表示される場合-以前に投稿されたアクションのスケジュールを解除します。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);
}
}
_
アプローチの欠点は、API 24から registerDefaultNetworkCallback()
が利用できることです。 ConnectivityManagerCompat
にも代替手段はありません。代わりに、API 21から利用可能な registerNetworkCallback()
を使用できます。
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();
}
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
です。以下のリンクを確認してください。
wifiまたは3gネットワークの状態が変更されたときのBroadcastReceiver
問題の解決に役立てば幸いです。もしそうなら、質問の終了を検討してください。