讀取網路狀態

Android 系統可讓應用程式瞭解網路連線的動態變化。請使用下列類別來追蹤及回應網路連線改變的情況:

  • ConnectivityManager 會向應用程式告知系統的連線狀態。
  • Network 類別代表裝置目前連結的其中一個網路。您可以使用 Network 物件做為鍵,以透過 ConnectivityManager 收集網路相關資訊,或繫結網路的通訊端。網路連線中斷時,Network 物件即無法使用。即使裝置之後重新連上同一設備,新的 Network 物件也會代表新網路。
  • LinkProperties 物件包含網路連結的相關資訊,例如 DNS 伺服器清單、本機 IP 位址,以及為該網路安裝的網路路徑。
  • NetworkCapabilities 物件包含網路屬性的相關資訊,例如傳輸方式 (Wi-Fi、行動網路、藍牙) 和網路的可用功能。舉例來說,您可以查詢此物件,藉此判斷網路是否能夠傳送多媒體訊息、是否透過網頁認證入口連線,或是否採用計量付費。

如果應用程式希望在任何時間點都能取得即時連線狀態,只要呼叫 ConnectivityManager 方法即可得知可用的網路類型。如果您要進行偵錯,以及偶爾檢查任何特定時間的可用連線數據匯報,則這些方法十分實用。

不過,同步處理的 ConnectivityManager 方法不會向應用程式告知呼叫後發生的任何狀況,因此無法讓您更新使用者介面。也無法在網路連線中斷或網路功能變更時調整應用程式行為。

網路連線隨時都可能發生變化,而大多數應用程式都需要即時掌握裝置的最新網路狀態。應用程式可以向 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

NetworkCapabilitiesLinkProperties 物件可以針對系統瞭解的網路提供所有屬性的相關資訊。

LinkProperties 物件可提供路徑、連結位址、介面名稱、Proxy 資訊 (如有) 和 DNS 伺服器的相關資訊。只要對 LinkProperties 物件呼叫相關方法,即可擷取所需資訊。

NetworkCapabilities 物件會封裝網路的「傳輸」及「功能」相關資訊。

傳輸是一種實體媒介的抽象層,而網路會在其上運作。常見的傳輸方式包括乙太網路、Wi-Fi 和行動裝置。VPN 和點對點 Wi-Fi 也可以做為傳輸方式。在 Android 中,網路可以同時提供多種傳輸方式。例如同時透過 Wi-Fi 和行動網路運作的 VPN。VPN 支援 Wi-Fi、行動裝置和 VPN 傳輸方式。如要確認網路是否支援特定傳輸方式,請搭配其中一個 NetworkCapabilities.TRANSPORT_* 常數使用 NetworkCapabilities.hasTransport(int) 方法。

功能可描述網路的屬性,例如 MMSNOT_METEREDINTERNET 等功能。具備 MMS 功能的網路可以收發多媒體訊息服務的訊息,但沒有此功能的網路就無法執行這項操作。具備 NOT_METERED 功能的網路不會向使用者收取資料費用。應用程式可使用 NetworkCapabilities.hasCapability(int) 方法與其中一個 NetworkCapabilities.NET_CAPABILITY_* 常數,確認網路是否具備適用功能。

最實用的 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_INTERNETNET_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) 搭配使用。這兩種方法提供的用途不同。

所有 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() 測量頻寬,並搭配使用 NetworkCapabilites.hasCapability(int)NET_CAPABILITY_NOT_METERED 引數來判斷是否為計量付費。詳情請參閱「NetworkCapabilities 和 LinkProperties」一節。

根據預設,系統會透過應用程式的連線執行緒呼叫回呼方法,這類執行緒與 ConnectivityManager 所用的執行緒不同。如果實作回呼需要進行較費時的作業,請使用變化版本 ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler),在個別背景工作執行緒上呼叫方法。

如果您不再需要使用回呼,請呼叫 ConnectivityManager.unregisterNetworkCallback(NetworkCallback) 來取消註冊。建議您透過主要活動的 onPause() 取消註冊,特別是在 onResume() 中註冊回呼的情況。

其他網路

儘管預設網路是大部分應用程式唯一的相關網路,但部分應用程式可能希望使用其他可用網路。為了瞭解這類情況,應用程式會建構符合需求的 NetworkRequest 並呼叫 ConnectivityManager.registerNetworkCallback(NetworkRequest, NetworkCallback)

這套程序與監聽預設網路類似,不過,雖然在任何特定時間只能有一個應用程式使用一個預設網路,但這個版本可讓應用程式同時查看所有可用網路,因此呼叫 onLost(Network) 表示網路已永久中斷連線,不代表它不再是預設網路。

應用程式會建立 NetworkRequest 以通知 ConnectivityManager 要監聽哪種類型的網路。以下範例說明如何為只使用非計量付費網路連線的應用程式建構 NetworkRequest

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);

這表示您的應用程式知道系統中任何關於非計量付費網路的所有變更。

對於預設網路回呼,系統提供了可接受 HandlerregisterNetworkCallback(NetworkRequest, NetworkCallback, Handler) 版本,因此不會載入應用程式的 Connectivity 執行緒。

如果回呼不再相關,請呼叫 ConnectivityManager.unregisterNetworkCallback(NetworkCallback)。一個應用程式可以同時註冊多個網路回呼。

為方便起見,NetworkRequest 物件包含多數應用程式需要的常用功能,包括:

編寫應用程式時,請檢查預設設定,確認是否與您的用途相符;如果您想在應用程式連上沒有這些功能的網路時收到通知,請清除預設設定。另一方面,請新增功能,避免當應用程式不會與其互動的網路發生任何連線異動時,應用程式會收到通知。

舉例來說,如果您的應用程式需要傳送多媒體訊息,請將 NET_CAPABILITY_MMS 新增至 NetworkRequest,以免系統通知所有網路皆無法傳送多媒體訊息。如果您的應用程式只要使用 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