連線到網路

如要在應用程式中執行網路作業,資訊清單必須包含下列權限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

安全網路通訊的最佳做法

將網路功能加入應用程式前,您需要確保應用程式內的資料和資訊在透過網路傳輸時安全無虞。如要這麼做的話,請按照下列網路安全性最佳做法操作:

  • 請盡量減少透過網路傳輸機密或個人使用者資料
  • 應用程式透過 SSL,傳送到所有網絡。
  • 建議您建立網路安全性設定,讓應用程式信任自訂憑證授權單位 (CA),或限制可信任通訊的 CA 系統組合。

如要進一步瞭解如何套用安全網路的原則,請參閱網路安全性提示

選擇 HTTP 用戶端

大多數具備網路連線的應用程式會使用 HTTP 傳送及接收資料。Android 平台包括 HttpsURLConnection 用戶端,其支援傳輸層安全標準 (TLS)、串流上傳和下載、可設定逾時、IPv6 和連線集區。

此外,某些第三方程式庫也提供更高階的網路作業 API。這些 API 可支援各種便利功能,例如將要求主體序列化,以及將回應主體反序列化。

  • Retrofit:這個類型安全的 HTTP 用戶端適用於 Square 的 JVM,以 OkHttp 為建構基礎。Retrofit 可讓您以宣告方式建立用戶端介面,同時支援多個序列化程式庫。
  • Ktor:這個 JetBrains 的 HTTP 用戶端全為 Kotlin 建構而成,採用協同程式提供的技術。Ktor 支援各種引擎、序列化程式和平台。

解析 DNS 查詢

搭載 Android 10 (API 級別 29) 以上版本的裝置內建支援功能,可同時透過明文查詢和「透過 TLS 執行 DNS」模式專門處理 DNS 查詢。DnsResolver API 提供一般的非同步解析,可讓您查詢 SRVNAPTR 和其他記錄類型。請注意,剖析回應會留在應用程式中執行。

在搭載 Android 9 (API 級別 28) 或以下版本的裝置上,平台 DNS 解析器僅支援 AAAAA 記錄。這可讓您查詢與名稱相關聯的 IP 位址,但不支援任何其他記錄類型。

如需以 NDK 為基礎的應用程式,請參閱 android_res_nsend 的說明。

使用存放區封裝網路作業

如要簡化執行網路作業的流程,並減少應用程式中各部分的程式碼重複,您可以使用存放區設計模板。存放區是一種資料處理作業的類別,也是針對某些特定資料或資源提供簡潔的 API 抽象層。

您可以使用 Retrofit 宣告介面,指定網路作業的 HTTP 方法、網址、引數和回應類型,如以下範例所示:

Kotlin

interface UserService {
    @GET("/users/{id}")
    suspend fun getUser(@Path("id") id: String): User
}

Java

public interface UserService {
    @GET("/user/{id}")
    Call<User> getUserById(@Path("id") String id);
}

在存放區類別中,函式可以封裝網路作業並公開結果。這個封裝可確保呼叫存放區的元件,並不需要知道資料的儲存方式。日後若資料儲存方式有所變更,也都只會侷限在存放區類別之中。舉例來說,您可能會想進行遠端變更 (例如更新 API 端點),或是想要導入本機快取,這些都屬於上述情況。

Kotlin

class UserRepository constructor(
    private val userService: UserService
) {
    suspend fun getUserById(id: String): User {
        return userService.getUser(id)
    }
}

Java

class UserRepository {
    private UserService userService;

    public UserRepository(
            UserService userService
    ) {
        this.userService = userService;
    }

    public Call<User> getUserById(String id) {
        return userService.getUser(id);
    }
}

為了避免建立沒有回應的 UI,請勿在主執行緒中執行網路作業。根據預設,Android 會要求您在主 UI 執行緒以外的執行緒上執行網路作業。如果您嘗試在主執行緒上執行網路作業,系統會擲回 NetworkOnMainThreadException

在上一個程式碼範例中,系統實際上並未觸發網路作業。UserRepository 的呼叫者必須使用協同程式或 enqueue() 函式來實作執行緒。詳情請參閱「從網際網路取得資料」程式碼研究室,瞭解如何使用 Kotlin 協同程式實作執行緒。

在設定變更後仍然有效

發生設定變更 (例如畫面旋轉) 時,系統會刪除您的片段或活動,再重新建立工作。由於片段活動的實例狀態中只能儲存少量資料,因此任何未儲存的資料都會遺失。如果發生這種情況,您可能需要再次發出網路要求。

您可以使用 ViewModel,確保資料在設定變更後仍然有效。ViewModel 元件的設計可讓您以注重生命週期的方式儲存及管理 UI 相關資料。如果您使用上述 UserRepositoryViewModel 即可發出必要的網路要求,並使用 LiveData 將結果提供給片段或活動:

Kotlin

class MainViewModel constructor(
    savedStateHandle: SavedStateHandle,
    userRepository: UserRepository
) : ViewModel() {
    private val userId: String = savedStateHandle["uid"] ?:
        throw IllegalArgumentException("Missing user ID")

    private val _user = MutableLiveData<User>()
    val user = _user as LiveData<User>

    init {
        viewModelScope.launch {
            try {
                // Calling the repository is safe as it moves execution off
                // the main thread
                val user = userRepository.getUserById(userId)
                _user.value = user
            } catch (error: Exception) {
                // Show error message to user
            }

        }
    }
}

Java

class MainViewModel extends ViewModel {

    private final MutableLiveData<User> _user = new MutableLiveData<>();
    LiveData<User> user = (LiveData<User>) _user;

    public MainViewModel(
            SavedStateHandle savedStateHandle,
            UserRepository userRepository
    ) {
        String userId = savedStateHandle.get("uid");
        Call<User> userCall = userRepository.getUserById(userId);
        userCall.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    _user.setValue(response.body());
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // Show error message to user
            }
        });
    }
}

如要進一步瞭解這個主題,請參閱下列相關指南: