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

Android は、接続の動的な変化についてアプリが把握できるようにします。接続の変更を追跡して対応するには、次のクラスを使用します。

  • ConnectivityManager は、システムの接続状態をアプリに通知します。
  • Network クラスは、デバイスが現在接続されているネットワークのいずれかを表します。Network オブジェクトをキーとして使用し、ConnectivityManager でネットワークに関する情報を収集したり、ネットワーク上のソケットをバインドしたりできます。ネットワークが切断されると、Network オブジェクトは使用できなくなります。デバイスが後で同じアプライアンスに再接続される場合も、新しい Network オブジェクトが新しいネットワークを表します。
  • LinkProperties オブジェクトには、そのネットワークについてインストールされている DNS サーバー、ローカル IP アドレス、ネットワーク ルートのリストなど、ネットワークのリンクに関する情報が格納されます。
  • NetworkCapabilities オブジェクトには、トランスポート(Wi-Fi、モバイル、Bluetooth)やネットワークの機能など、ネットワークのプロパティに関する情報が格納されます。たとえば、オブジェクトにクエリして、ネットワークが MMS を送信できるかどうか、キャプティブ ポータルの背後にあるか、従量制であるかどうかを判断できます。

任意の時点の接続状態を知る必要があるアプリでは、ConnectivityManager メソッドを呼び出して、利用可能なネットワークの種類を調べることができます。こうしたメソッドは、デバッグや、任意の時点で利用可能な接続のスナップショットを確認する際に役立ちます。ただし、同期型の ConnectivityManager メソッドは、呼び出し後に発生したことをアプリに通知しないため、ネットワークの切断やネットワーク機能の変更に基づいて、UI を更新したりアプリの動作を調整したりすることはできません。

接続はいつでも変更される可能性があり、ほとんどのアプリではデバイスのネットワーク状態を常に最新に維持する必要があるため、アプリで把握する必要のある変化について警告を受け取れるように、ConnectivityManager でコールバックを登録できます。コールバックを使用すると、頻繁な更新を見逃すおそれのある高価なポーリングに頼ることなく、関連する接続の変更に即座に対応できます。

瞬間的な状態を取得する

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 が挙げられます。このようなネットワークには、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 に通知します。たとえば、アプリが定額制のインターネット接続のみを必要とする場合、次のようになります。

Kotlin

val request = NetworkRequest.Builder()
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NET_CAPABILITY_INTERNET)
  .build()

connectivityManager.registerNetworkCallback(request, myNetworkCallback)

Java

NetworkRequest request = new NetworkRequest.Builder()
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NET_CAPABILITY_INTERNET)
  .build();

connectivityManager.registerNetworkCallback(request, myNetworkCallback);

これにより、システム上の定額制ネットワークに関するすべての変更がアプリで認識されます。

デフォルト ネットワークのコールバックについては、アプリの Connectivity スレッドを読み込まないように Handler を受け入れるバージョンの registerNetworkCallback(NetworkRequest, NetworkCallback, Handler) があります。コールバックが不要になった場合は、ConnectivityManager.unregisterNetworkCallback(NetworkCallback) を呼び出します。アプリは複数のネットワーク コールバックを同時に登録できます。

便宜上、NetworkRequest オブジェクトには、次のようなほとんどのアプリで必要となる一般的なケーパビリティが含まれています。

アプリを作成するときは、ユースケースに合っているかどうかデフォルトを確認します。こうしたケーパビリティがないネットワークについても、アプリに通知する必要がある場合はクリアします。逆に、アプリが関係しないネットワークの接続変更を求められないようにするための機能を追加する必要があります。

たとえば、アプリで MMS メッセージを送信する必要がある場合、MMS を送信できないすべてのネットワークについて通知されないようにするには NET_CAPABILITY_MMSNetworkRequest に追加します。PSP 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 を呼び出します。