ネットワークの状態を読み取る

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) メソッドを使用します。

ケーパビリティは、ネットワークの特性を表します。ケーパビリティの例としては、MMSNOT_METEREDINTERNET などがあります。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_MMSNetworkRequest に追加して、MMS メッセージを送信できないすべてのネットワークについて通知されないようにします。アプリが P2P Wi-Fi 接続のみを必要とする場合は、TRANSPORT_WIFI_AWARE を追加します。NET_CAPABILITY_INTERNETNET_CAPABILITY_VALIDATED は、インターネット上のサーバーを使用してデータを転送する機能が必要な場合に便利です。

コールバック シーケンスの例

このセクションでは、モバイル接続しているデバイスでデフォルトのコールバックと通常のコールバックを両方とも登録した場合に、アプリが取得するコールバックのシーケンスについて説明します。この例で、デバイスは適切な Wi-Fi アクセス ポイントに接続した後、切断します。また、デバイスでモバイルデータを常にオンにする設定が有効になっていることも前提としています。

タイムラインは次のとおりです。

  1. アプリが registerNetworkCallback() を呼び出すと、コールバックはモバイル ネットワークの onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() からの呼び出しをすぐに受け取ります。これは、モバイル ネットワークしか利用できないためです。別のネットワークが利用可能であれば、アプリは他のネットワークのコールバックも受け取ります。

    登録ネットワークのコールバック イベントと、そのイベントによってトリガーされるコールバックを示す状態図
    図 1. registerNetworkCallback() を呼び出した後のアプリの状態

  2. 次に、アプリは registerDefaultNetworkCallback() を呼び出します。モバイル ネットワークがデフォルト ネットワークであるため、デフォルト ネットワークのコールバックは、モバイル ネットワークの onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() に対する呼び出しを受け取り始めます。デフォルト以外の別のネットワークがある場合、アプリはデフォルト以外のネットワークの呼び出しを受け取れません。

    デフォルト ネットワークのコールバック イベントと、そのイベントによってトリガーされるコールバックを示す状態図
    図 2. デフォルト ネットワークを登録した後のアプリの状態

  3. その後、デバイスが(定額制の)Wi-Fi ネットワークに接続します。通常のネットワーク コールバックは、Wi-Fi ネットワークの onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() に対する呼び出しを受け取ります。

    アプリが新しいネットワークに接続されたときにトリガーされるコールバックを示す状態図
    図 3. 定額制の Wi-Fi ネットワークに接続した後のアプリの状態

  4. ここで、Wi-Fi ネットワークの検証に時間がかかることがあります。この場合、通常のネットワーク コールバックの onNetworkCapabilitiesChanged() 呼び出しに、ケーパビリティ NET_CAPABILITY_VALIDATED は含まれません。しばらくすると、onNetworkCapabilitiesChanged() に対する呼び出しを受け取ります。新しいケーパビリティには NET_CAPABILITY_VALIDATED が含まれます。ほとんどの場合、検証は非常に迅速です。

    Wi-Fi ネットワークが検証されると、システムはモバイル ネットワークよりも Wi-Fi ネットワークを優先します。これは主に、定額制であるためです。Wi-Fi ネットワークがデフォルト ネットワークになるため、デフォルト ネットワークのコールバックは、Wi-Fi ネットワークの onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() に対する呼び出しを受け取ります。モバイル ネットワークはバックグラウンドとなり、通常のネットワーク コールバックはモバイル ネットワークの onLosing() に対する呼び出しを受け取ります。

    この例では、デバイスでモバイルデータが常にオンになっていることを前提としているため、モバイル ネットワーク接続が切断されることはありません。この設定がオフになっている場合、しばらくするとモバイル ネットワークが切断され、通常のネットワーク コールバックが onLost() に対する呼び出しを受け取ります。

    Wi-Fi ネットワーク接続の検証時にトリガーされるコールバックを示す状態図
    図 4. Wi-Fi ネットワークを検証した後のアプリの状態

  5. その後、範囲外になったためにデバイスが Wi-Fi から突然切断されます。Wi-Fi が切断されるため、通常のネットワーク コールバックは Wi-Fi の onLost() に対する呼び出しを受け取ります。モバイル ネットワークが新しいデフォルト ネットワークであるため、デフォルト ネットワークのコールバックは、モバイル ネットワークの onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() に対する呼び出しを受け取ります。

    Wi-Fi ネットワーク接続が失われたときにトリガーされるコールバックを示す状態図
    図 5. Wi-Fi ネットワークから切断された後のアプリの状態

[モバイルデータを常にオンにする] 設定がオフになっている場合、Wi-Fi から切断されると、デバイスはモバイル ネットワークへの再接続を試行します。状況は似ていますが、onAvailable() 呼び出しに少し余計に時間がかかり、モバイル ネットワークが利用可能になるため、通常のネットワーク コールバックも onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() に対する呼び出しを受け取ります。

データ転送でのネットワーク使用に関する制限

ネットワーク コールバックでネットワークを確認できても、アプリでそのネットワークをデータ転送に使用できるというわけではありません。ネットワークの中には、インターネット接続が提供されないものもあれば、特権アプリに制限されているものもあります。インターネット接続について確認するには、NET_CAPABILITY_INTERNETNET_CAPABILITY_VALIDATED をご覧ください。

バックグラウンド ネットワークの使用も、権限チェックの対象となります。アプリでバックグラウンド ネットワークを使用する場合は、CHANGE_NETWORK_STATE 権限が必要です。

アプリにこの権限があると、システムは、デバイスが Wi-Fi ネットワークに接続されているとき、モバイル ネットワークなどの現在使用していないネットワークを使用するよう試みます。このようなアプリは、ネットワークの使用時に呼び出す ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) を指定して NetworkCallback を呼び出します。