Android は、接続の動的な変化についてアプリが把握できるようにします。接続の変更を追跡して対応するには、次のクラスを使用します。
ConnectivityManagerは、システムの接続状態をアプリに通知します。Networkクラスは、デバイスが接続されているネットワークのいずれかを表します。Networkオブジェクトをキーとして使用し、ConnectivityManagerでネットワークに関する情報を収集したり、ネットワーク上のソケットをバインドしたりできます。ネットワークが切断されると、Networkオブジェクトは使用できなくなります。デバイスが後で同じアプライアンスに再接続される場合も、新しいNetworkオブジェクトが新しいネットワークを表します。LinkPropertiesオブジェクトには、そのネットワークについてインストールされている DNS サーバー、ローカル IP アドレス、ネットワーク ルートのリストなど、ネットワークのリンクに関する情報が格納されます。NetworkCapabilitiesオブジェクトには、トランスポート(Wi-Fi、モバイル、Bluetooth)やネットワークの機能など、ネットワークのプロパティに関する情報が格納されます。たとえば、オブジェクトにクエリして、ネットワークが MMS を送信できるかどうか、キャプティブ ポータルの背後にあるか、従量制であるかどうかを判断できます。
任意の時点の接続状態を知る必要があるアプリでは、ConnectivityManager のメソッドを呼び出して、利用可能なネットワークの種類を調べることができます。こうしたメソッドは、デバッグや、任意の時点で利用可能な接続のスナップショットを確認する際に役立ちます。
ただし、同期型の ConnectivityManager メソッドは、呼び出し後に発生したことをアプリに通知しないため、UI を更新できません。また、ネットワークの切断やネットワーク機能の変更に基づいて、アプリの動作を調整することもできません。
接続はいつでも変更される可能性があり、ほとんどのアプリはデバイスのネットワーク状態を常に最新の状態に保つ必要があります。アプリで把握する必要のある変化について警告を受け取れるように、ConnectivityManager でコールバックを登録できます。コールバックを使用すると、頻繁な更新を見逃すおそれのある高価なポーリングに頼ることなく、関連する接続の変更に即座に対応できます。
NetworkCallback などを使用してデバイスの接続状態を確認する際、特定の権限が必要になることはありません。ただし、ネットワークによっては特定の権限が必要になります。たとえば、アプリが使用できない制限されたネットワークで、バックグラウンド ネットワークにバインドするには、CHANGE_NETWORK_STATE 権限が必要です。また、一部の呼び出しを行うために特定の権限が必要になることがあります。詳細については、各呼び出しの詳しいドキュメントをご覧ください。
瞬間的な状態を取得する
Android 搭載デバイスは同時に多数の接続を維持できます。現在のネットワークの状態に関する情報を取得するには、まず、ConnectivityManager のインスタンスを取得します。
Kotlin
val connectivityManager = getSystemService(ConnectivityManager::class.java)
Java
ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
次に、このインスタンスを使用して、アプリの現在のデフォルト ネットワークへの参照を取得します。
Kotlin
val currentNetwork = connectivityManager.getActiveNetwork()
Java
Network currentNetwork = connectivityManager.getActiveNetwork();
ネットワークへの参照を指定することで、アプリはそのネットワークに関する情報をリクエストできます。
Kotlin
val caps = connectivityManager.getNetworkCapabilities(currentNetwork) val linkProperties = connectivityManager.getLinkProperties(currentNetwork)
Java
NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(currentNetwork); LinkProperties linkProperties = connectivityManager.getLinkProperties(currentNetwork);
便利な機能を利用するには、NetworkCallback を登録します。ネットワーク コールバックの登録について詳しくは、ネットワーク イベントをリッスンするをご覧ください。
NetworkCapabilities と LinkProperties
NetworkCapabilities オブジェクトと LinkProperties オブジェクトは、システムがネットワークについて認識しているすべての属性に関する情報を提供します。
LinkProperties オブジェクトは、ルート、リンクアドレス、インターフェース名、プロキシ情報(存在する場合)、DNS サーバーを認識します。LinkProperties オブジェクトの関連するメソッドを呼び出して、必要な情報を取得します。
NetworkCapabilities オブジェクトは、ネットワーク トランスポートとそのケーパビリティに関する情報をカプセル化します。
トランスポートは、ネットワークが動作する物理メディアを抽象化したものです。トランスポートの一般的な例としては、イーサネット、Wi-Fi、モバイルがあります。VPN とピアツーピア Wi-Fi もトランスポートとされることがあります。Android では、1 つのネットワークで複数のトランスポートを同時に使用できます。たとえば、Wi-Fi ネットワークとモバイル ネットワークの両方で動作する VPN です。VPN には、Wi-Fi、モバイル、VPN のトランスポートがあります。ネットワークに特定のトランスポートがあるかどうかを確認するには、NetworkCapabilities.TRANSPORT_* 定数のいずれかを指定して NetworkCapabilities.hasTransport(int) メソッドを使用します。
ケーパビリティは、ネットワークの特性を表します。ケーパビリティの例としては、MMS、NOT_METERED、INTERNET などがあります。MMS ケーパビリティを備えたネットワークでは、マルチメディア メッセージング サービスのメッセージを送受信できますが、このケーパビリティのないネットワークでは送受信できません。NOT_METERED ケーパビリティを備えたネットワークでは、データに対する課金は発生しません。NetworkCapabilities.NET_CAPABILITY_* 定数のいずれかを指定して NetworkCapabilities.hasCapability(int) メソッドを使用すると、該当するケーパビリティをアプリで確認できます。
有用な NET_CAPABILITY_* 定数としては、次のものがあります。
NET_CAPABILITY_INTERNET: インターネットにアクセスするようにネットワークがセットアップされていることを示します。これはセットアップについてのものであり、公開サーバーに到達するための実際の機能ではありません。たとえば、インターネットにアクセスできるようネットワークがセットアップされていても、キャプティブ ポータルの対象となる場合があります。通常、携帯通信会社のモバイル ネットワークには
INTERNETケーパビリティがありますが、ローカル P2P Wi-Fi ネットワークにはありません。実際の接続については、NET_CAPABILITY_VALIDATEDをご覧ください。NET_CAPABILITY_NOT_METERED: ネットワークが従量制でないことを示します。金銭的コスト、データの上限、またはバッテリーやパフォーマンスの問題が原因で、ネットワーク接続でデータ使用量が多くなることをユーザーが気にしている場合、ネットワークは従量制として分類されます。NET_CAPABILITY_NOT_VPN: ネットワークがバーチャル プライベート ネットワークでないことを示します。NET_CAPABILITY_VALIDATED: プローブされたときに、公開インターネットへの実際のアクセスを提供していることを示します。キャプティブ ポータルの背後にあるネットワーク、またはドメイン名解決を提供しないネットワークには、このケーパビリティはありません。これは、実際にアクセスを提供しているネットワークについてシステムが知り得る最も近い情報です。ただし、検証されたネットワークは、原則として、IP ベースのフィルタリングの対象となったり、電波状況の悪さなどが原因で接続が突然失われたりする可能性があります。NET_CAPABILITY_CAPTIVE_PORTAL: ネプローブされたときに、ネットワークにキャプティブ ポータルがあることを示します。
より特化したアプリで必要となる可能性のあるケーパビリティは他にもあります。詳細については、NetworkCapabilities.hasCapability(int) のパラメータ定義をご覧ください。
ネットワークのケーパビリティは随時変更される可能性があります。キャプティブ ポータルが検出されると、ユーザーにログインを促す通知が表示されます。その間、ネットワークに NET_CAPABILITY_INTERNET ケーパビリティと NET_CAPABILITY_CAPTIVE_PORTAL ケーパビリティはありますが、NET_CAPABILITY_VALIDATED ケーパビリティはありません。
ユーザーが操作してキャプティブ ポータル ページにログインすると、デバイスが公開インターネットにアクセスできるようになり、ネットワークは NET_CAPABILITY_VALIDATED ケーパビリティを得て、NET_CAPABILITY_CAPTIVE_PORTAL ケーパビリティを失います。
同様に、ネットワークのトランスポートも動的に変更されることがあります。たとえば VPN は、基となるネットワークをモバイルから Wi-Fi に切り替えるなど、見つかったばかりの高速なネットワークを使用するように再構成する可能性があります。この場合、ネットワークは TRANSPORT_CELLULAR トランスポートを失い、TRANSPORT_WIFI トランスポートを得ますが、TRANSPORT_VPN トランスポートは維持します。
ネットワーク イベントをリッスンする
ネットワーク イベントについて調べるには、NetworkCallback クラスを、ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback)、ConnectivityManager.registerNetworkCallback(NetworkCallback) とともに使用します。これら 2 つのメソッドは、それぞれ異なる目的で使用します。
どの Android アプリにも、システムによって決定されるデフォルト ネットワークがあります。システムは通常、従量制ネットワークよりも定額制ネットワークを優先し、低速のネットワークよりも高速のネットワークを優先します。
アプリが HttpsURLConnection などのネットワーク リクエストを発行すると、システムはデフォルト ネットワークを使用してこのリクエストに応じます。アプリは他のネットワークでもトラフィックを送信できます。詳細については、追加のネットワークのセクションをご覧ください。
デフォルト ネットワークとして設定されたネットワークは、アプリの存続期間中、随時変更される可能性があります。一般的な例としては、デバイスが、既知でアクティブな、モバイルよりも高速な定額制 Wi-Fi アクセス ポイントの範囲内に入った場合が挙げられます。デバイスはこのアクセス ポイントに接続し、すべてのアプリのデフォルト ネットワークを新しい Wi-Fi ネットワークに切り替えます。
新しいネットワークがデフォルトになると、アプリが開く新しい接続で、このネットワークが使用されます。しばらくすると、前のデフォルト ネットワーク上の残りの接続はすべて強制的に終了されます。デフォルト ネットワークが変更されるタイミングをアプリで認識することが重要な場合は、デフォルト ネットワークのコールバックを次のように登録します。
Kotlin
connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network : Network) { Log.e(TAG, "The default network is now: " + network) } override fun onLost(network : Network) { Log.e(TAG, "The application no longer has a default network. The last default network was " + network) } override fun onCapabilitiesChanged(network : Network, networkCapabilities : NetworkCapabilities) { Log.e(TAG, "The default network changed capabilities: " + networkCapabilities) } override fun onLinkPropertiesChanged(network : Network, linkProperties : LinkProperties) { Log.e(TAG, "The default network changed link properties: " + linkProperties) } })
Java
connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Log.e(TAG, "The default network is now: " + network); } @Override public void onLost(Network network) { Log.e(TAG, "The application no longer has a default network. The last default network was " + network); } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { Log.e(TAG, "The default network changed capabilities: " + networkCapabilities); } @Override public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { Log.e(TAG, "The default network changed link properties: " + linkProperties); } });
新しいネットワークがデフォルトになると、アプリは新しいネットワークの onAvailable(Network) に対する呼び出しを受け取ります。接続の変更に適切に対応するには、onCapabilitiesChanged(Network,NetworkCapabilities)、onLinkPropertiesChanged(Network,LinkProperties) のいずれか、または両方を実装します。
registerDefaultNetworkCallback() で登録されたコールバックの場合、onLost() は、そのネットワークがデフォルト ネットワークであるというステータスを失ったことを示します。接続が解除されている可能性があります。
NetworkCapabilities.hasTransport(int) をクエリすると、デフォルト ネットワークで使用されているトランスポートについて確認できますが、ネットワークの帯域幅や従量制を表すものとしては不十分です。アプリで、Wi-Fi が常に定額制であるという仮定や、常にモバイルよりも帯域幅が広いという仮定はできません。
代わりに、帯域幅の測定には NetworkCapabilities.getLinkDownstreamBandwidthKbps() を使用し、従量制の判断には NET_CAPABILITY_NOT_METERED 引数を指定して NetworkCapabilites.hasCapability(int) を使用します。詳細については、NetworkCapabilities と LinkProperties のセクションをご覧ください。
デフォルトでは、コールバック メソッドはアプリの接続スレッド(ConnectivityManager が使用する別のスレッド)で呼び出されます。コールバックの実装でさらに作業が必要な場合は、バリアント ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler) を使用して別のワーカー スレッドで呼び出します。
コールバックが不要になった場合は、ConnectivityManager.unregisterNetworkCallback(NetworkCallback) を呼び出してコールバックの登録を解除します。メイン アクティビティの onPause() は、特にコールバックを onResume() で登録する場合、これを行うために適しています。
追加のネットワーク(高度なユースケース)
ほとんどのアプリではデフォルト ネットワークが唯一の関連ネットワークですが、アプリによっては、利用可能な他のネットワークが必要となるものもあります。これを確認するには、アプリでニーズに合う NetworkRequest を作成し、ConnectivityManager.registerNetworkCallback(NetworkRequest, NetworkCallback) を呼び出します。
このプロセスは、デフォルト ネットワークをリッスンする場合に似ています。ただし、任意の時点でアプリに適用されるデフォルト ネットワークは 1 つだけであるのに対し、ここではアプリで利用可能なネットワークをすべて同時に認識できます。そのため、onLost(Network) に対する呼び出しは、ネットワークが完全に切断されたということであって、デフォルトではなくなったということではありません。
アプリは NetworkRequest を作成して、リッスンするネットワークの種類を ConnectivityManager に通知します。次の例は、定額制のインターネット接続のみを必要とするアプリの NetworkRequest を作成する方法を示しています。
Kotlin
val request = NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build() connectivityManager.registerNetworkCallback(request, myNetworkCallback)
Java
NetworkRequest request = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build(); connectivityManager.registerNetworkCallback(request, myNetworkCallback);
つまり、システム上の定額制ネットワークに関するすべての変更がアプリで認識されます。
デフォルト ネットワークのコールバックについては、アプリの Connectivity スレッドを読み込まないように Handler を受け入れるバージョンの registerNetworkCallback(NetworkRequest, NetworkCallback, Handler) があります。
コールバックが不要になった場合は、ConnectivityManager.unregisterNetworkCallback(NetworkCallback) を呼び出します。アプリは複数のネットワーク コールバックを同時に登録できます。
便宜上、NetworkRequest オブジェクトには、次のようなほとんどのアプリで必要となる一般的なケーパビリティが含まれています。
アプリを作成するときは、ユースケースに合っているかどうかデフォルトの値を確認します。こうしたケーパビリティがないネットワークについてアプリに通知する場合は、削除します。一方、アプリが関知しないネットワークの接続変更について通知されないようにするには、ケーパビリティを追加します。
たとえばアプリで MMS メッセージを送信する必要がある場合は、NET_CAPABILITY_MMS を NetworkRequest に追加して、MMS メッセージを送信できないすべてのネットワークについて通知されないようにします。アプリが P2P Wi-Fi 接続のみを必要とする場合は、TRANSPORT_WIFI_AWARE を追加します。NET_CAPABILITY_INTERNET と NET_CAPABILITY_VALIDATED は、インターネット上のサーバーを使用してデータを転送する機能が必要な場合に便利です。
コールバック シーケンスの例
このセクションでは、モバイル接続しているデバイスでデフォルトのコールバックと通常のコールバックを両方とも登録した場合に、アプリが取得するコールバックのシーケンスについて説明します。この例で、デバイスは適切な Wi-Fi アクセス ポイントに接続した後、切断します。また、デバイスでモバイルデータを常にオンにする設定が有効になっていることも前提としています。
タイムラインは次のとおりです。
アプリが
registerNetworkCallback()を呼び出すと、コールバックはモバイル ネットワークのonAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged()からの呼び出しをすぐに受け取ります。これは、モバイル ネットワークしか利用できないためです。別のネットワークが利用可能であれば、アプリは他のネットワークのコールバックも受け取ります。
図 1.registerNetworkCallback()を呼び出した後のアプリの状態次に、アプリは
registerDefaultNetworkCallback()を呼び出します。モバイル ネットワークがデフォルト ネットワークであるため、デフォルト ネットワークのコールバックは、モバイル ネットワークのonAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged()に対する呼び出しを受け取り始めます。デフォルト以外の別のネットワークがある場合、アプリはデフォルト以外のネットワークの呼び出しを受け取れません。
図 2. デフォルト ネットワークを登録した後のアプリの状態その後、デバイスが(定額制の)Wi-Fi ネットワークに接続します。通常のネットワーク コールバックは、Wi-Fi ネットワークの
onAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged()に対する呼び出しを受け取ります。
図 3. 定額制の Wi-Fi ネットワークに接続した後のアプリの状態ここで、Wi-Fi ネットワークの検証に時間がかかることがあります。この場合、通常のネットワーク コールバックの
onNetworkCapabilitiesChanged()呼び出しに、ケーパビリティNET_CAPABILITY_VALIDATEDは含まれません。しばらくすると、onNetworkCapabilitiesChanged()に対する呼び出しを受け取ります。新しいケーパビリティにはNET_CAPABILITY_VALIDATEDが含まれます。ほとんどの場合、検証は非常に迅速です。Wi-Fi ネットワークが検証されると、システムはモバイル ネットワークよりも Wi-Fi ネットワークを優先します。これは主に、定額制であるためです。Wi-Fi ネットワークがデフォルト ネットワークになるため、デフォルト ネットワークのコールバックは、Wi-Fi ネットワークの
onAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged()に対する呼び出しを受け取ります。モバイル ネットワークはバックグラウンドとなり、通常のネットワーク コールバックはモバイル ネットワークのonLosing()に対する呼び出しを受け取ります。この例では、デバイスでモバイルデータが常にオンになっていることを前提としているため、モバイル ネットワーク接続が切断されることはありません。この設定がオフになっている場合、しばらくするとモバイル ネットワークが切断され、通常のネットワーク コールバックが
onLost()に対する呼び出しを受け取ります。
図 4. Wi-Fi ネットワークを検証した後のアプリの状態その後、範囲外になったためにデバイスが Wi-Fi から突然切断されます。Wi-Fi が切断されるため、通常のネットワーク コールバックは Wi-Fi の
onLost()に対する呼び出しを受け取ります。モバイル ネットワークが新しいデフォルト ネットワークであるため、デフォルト ネットワークのコールバックは、モバイル ネットワークのonAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged()に対する呼び出しを受け取ります。
図 5. Wi-Fi ネットワークから切断された後のアプリの状態
[モバイルデータを常にオンにする] 設定がオフになっている場合、Wi-Fi から切断されると、デバイスはモバイル ネットワークへの再接続を試行します。状況は似ていますが、onAvailable() 呼び出しに少し余計に時間がかかり、モバイル ネットワークが利用可能になるため、通常のネットワーク コールバックも onAvailable()、onNetworkCapabilitiesChanged()、onLinkPropertiesChanged() に対する呼び出しを受け取ります。
データ転送でのネットワーク使用に関する制限
ネットワーク コールバックでネットワークを確認できても、アプリでそのネットワークをデータ転送に使用できるというわけではありません。ネットワークの中には、インターネット接続が提供されないものもあれば、特権アプリに制限されているものもあります。インターネット接続について確認するには、NET_CAPABILITY_INTERNET と NET_CAPABILITY_VALIDATED をご覧ください。
バックグラウンド ネットワークの使用も、権限チェックの対象となります。アプリでバックグラウンド ネットワークを使用する場合は、CHANGE_NETWORK_STATE 権限が必要です。
アプリにこの権限があると、システムは、デバイスが Wi-Fi ネットワークに接続されているとき、モバイル ネットワークなどの現在使用していないネットワークを使用するよう試みます。このようなアプリは、ネットワークの使用時に呼び出す ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) を指定して NetworkCallback を呼び出します。