這個程式碼研究室是 Android Kotlin 基礎知識課程的一部分。如果您按部就班完成程式碼研究室,就能充分體驗到本課程的價值。所有課程程式碼研究室都列在 Android Kotlin 基礎知識程式碼研究室到達網頁。
簡介
您建構的幾乎所有 Android 應用程式,在某個時間點都需要連線至網際網路。在本程式碼研究室和後續的程式碼研究室中,您會建構一個應用程式,連線至網路服務來擷取及顯示資料。您也會運用先前程式碼研究室中學到的 ViewModel、LiveData 和 RecyclerView 知識。
在本程式碼研究室中,您將使用社群開發的程式庫建構網路層。這樣可大幅簡化資料和圖片擷取作業,還可讓應用程式遵循部分 Android 最佳做法,例如在背景執行緒上載入圖片,以及快取已載入的圖片。對於程式碼中的非同步或非阻塞區段 (例如與網路服務層通訊),您將修改應用程式以使用 Kotlin 的協同程式。若網際網路速度緩慢或無法使用,您也可更新應用程式的使用者介面,讓使用者隨時掌握狀況。
須知事項
- 如何建立及使用片段。
 - 如何瀏覽各個片段,以及使用 
safeArgs在片段之間傳遞資料。 - 如何使用架構元件,包括 
ViewModel、ViewModelProvider.Factory、LiveData和LiveData轉換。 - 如何使用協同程式處理長時間執行的工作。
 
課程內容
執行步驟
- 修改範例應用程式,發出 Web 服務 API 要求並處理回應。
 - 使用 Retrofit 程式庫為應用程式實作網路層。
 - 使用 Moshi 程式庫,將網路服務中的 JSON 回應剖析至應用程式的即時資料。
 - 使用 Retrofit 提供的協同程式支援簡化程式碼。
 
在本程式碼研究室 (和後續程式碼研究室) 中,您將使用名為 MarsRealEstate 的範例應用程式,顯示火星上的待售地產。此應用程式會連線至網路服務,以擷取和顯示房地產資料,包括價格和房地產是否待售或待租等詳細資料。代表各個屬性的圖片是自 NASA 火星漫遊者擷取的火星實景相片。

您在本程式碼研究室中建構的應用程式版本不會包含許多視覺刷新作業,而會著重於應用程式的網路層,以便連線至網際網路,並透過網路服務下載原始屬性資料。為確保系統正確擷取和剖析資料,請直接在文字檢視區塊中,顯示火星上的房源數量:

。
MarsRealEstate 應用程式的架構有兩個主要模組:
- 總覽片段,其中包含以 
RecyclerView建構的縮圖屬性圖片格線。 - 詳細資料檢視畫面片段,內含各項屬性的相關資訊。
 

應用程式的每個片段都有一個 ViewModel。在本程式碼研究室中,您會建立網路服務層,而 ViewModel 會直接與該網路層通訊。這與您在先前的程式碼研究室中,讓 ViewModel 與 Room 資料庫通訊時的做法類似。
總覽 ViewModel 負責發出網路呼叫,以取得火星房地產資訊。詳細資料 ViewModel 會保留詳細資料片段中顯示的單一火星房地產詳細資料。針對每個 ViewModel,您會使用 LiveData 搭配生命週期感知資料繫結,在資料變更時更新應用程式 UI。
您可以使用 Navigation 元件在兩個片段之間導覽,並將所選屬性做為引數傳遞。
在這項工作中,您將下載並執行 MarsRealEstate 的範例應用程式,並熟悉專案結構。
步驟 1:探索片段和導覽功能
- 下載 MarsRealEstate 範例應用程式,並在 Android Studio 中開啟。
 - 檢查 
app/java/MainActivity.kt。應用程式會為這兩個畫面使用片段,因此活動的唯一工作是載入活動的版面配置。 - 檢查 
app/res/layout/activity_main.xml。活動版面配置是兩個片段的代管主機,定義於導覽檔案中。這個版面配置會使用nav_graph資源,例項化NavHostFragment及其相關聯的導覽控制器。 - 開啟 
app/res/navigation/nav_graph.xml。您可以在這裡查看這兩個片段之間的導覽關係。導覽圖StartDestination指向overviewFragment,因此應用程式啟動時,系統會執行個體化總覽片段。 
步驟 2:探索 Kotlin 來源檔案和資料繫結
- 在「Project」窗格中,依序展開「app」>「java」。請注意,MarsRealEstate 應用程式有三個套件資料夾:
detail、network和overview。這些對應於應用程式的三個主要元件:總覽和詳細資料片段,以及網路層的程式碼。
   - 開啟 
app/java/overview/OverviewFragment.kt。OverviewFragment會延遲初始化OverviewViewModel,也就是說,系統會在首次使用OverviewViewModel時建立該項目。 - 檢查 
onCreateView()方法。這個方法會使用資料繫結加載fragment_overview版面配置,將繫結生命週期擁有者設為自己 (this),並在binding物件中將viewModel變數設為自己。由於我們已設定生命週期擁有者,系統會自動觀察資料繫結中使用的任何LiveData是否有任何變更,並據以更新 UI。 - 開啟 
app/java/overview/OverviewViewModel。由於回應是LiveData,且我們已為繫結變數設定生命週期,因此回應的任何變更都會更新應用程式 UI。 - 檢查 
init區塊。建立ViewModel時,系統會呼叫getMarsRealEstateProperties()方法。 - 檢查 
getMarsRealEstateProperties()方法。在這個入門應用程式中,這個方法包含預留位置回應。本程式碼研究室的目標,在於使用從網際網路取得的實際資料來更新ViewModel中的回應LiveData。 - 開啟 
app/res/layout/fragment_overview.xml。這是您在本程式碼研究室中使用的總覽片段版面配置,其中包含檢視模型適用的資料繫結。這會匯入OverviewViewModel,然後將ViewModel的回應繫結至TextView。在後續的程式碼研究室中,您會將文字檢視區塊替換為RecyclerView中的圖片格線。 - 編譯並執行應用程式。您在目前版本的應用程式中只會看到起始回應:「Set the Mars API Response here!」(在此設定火星 API 回應!)
 
 
火星房地產資料會儲存在網路伺服器中,以 REST Web 服務的形式提供。使用 REST 架構的網路服務,均使用標準網路元件和通訊協定建構而成。
您可以透過 URI,以標準化的方式向網路服務傳送要求。熟悉的網站網址實際上屬於 URI 類型,在本課程中會交替使用這兩者。舉例來說,在本課程的應用程式中,您將從下列伺服器擷取所有資料:
https://android-kotlin-fun-mars-server.appspot.com
在瀏覽器中輸入下列網址,即可取得火星上所有可用的房地產資源清單!
https://android-kotlin-fun-mars-server.appspot.com/realestate
網路服務的回應通常會採用 JSON 格式,這是一種代表結構化資料的交換格式。我們將在下一個工作中進一步瞭解 JSON,但簡單來說,JSON 物件是一組鍵/值組合,有時也稱為「字典」、「雜湊對應」或「關聯陣列」。一組 JSON 物件稱為 JSON 陣列,而您從網路服務取得的回應就是這個陣列。
如要讓應用程式取得此資料,應用程式需要建立網路連線並與該伺服器通訊,然後接收回應資料並剖析為應用程式可用的格式。在本程式碼研究室中,您將使用名為 Retrofit 的 REST 用戶端程式庫建立連線。
步驟 1:將 Retrofit 依附元件新增至 Gradle
- 開啟「build.gradle (Module: app)」。
 - 在 
dependencies區段中,為 Retrofit 程式庫新增以下幾行: 
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
請注意,版本號碼是在專案 Gradle 檔案中分別定義。第一個依附元件適用於 Retrofit 2 程式庫本身,第二個依附元件則用於 Retrofit 純量轉換工具。此轉換工具可讓 Retrofit 以 String 的形式傳回 JSON 結果。這兩個程式庫會搭配運作。
- 按一下「Sync Now」,使用新依附元件重新建構專案。
 
步驟 2:實作 MarsApiService
Retrofit 會根據 Web 服務的內容,為應用程式建立網路 API。這會從網路服務擷取資料,並透過獨立的轉換工具程式庫轉送資料。該程式庫瞭解資料解碼方式,且會以實用物件的形式傳回資料。Retrofit 內建支援 XML 和 JSON 等熱門網路資料格式。Retrofit 最終會為您建立大部分的網路層,包括在背景執行緒中執行要求等重要詳細資訊。
MarsApiService 類別會保留應用程式的網路層,也就是 ViewModel 用來與網路服務通訊的 API。您將在這個類別中實作 Retrofit 服務 API。
- 開啟 
app/java/network/MarsApiService.kt。目前檔案只包含一項內容:Web 服務的基準網址常數。 
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"- 在該常數下方,使用 Retrofit 建構工具建立 Retrofit 物件。依要求匯入 
retrofit2.Retrofit和retrofit2.converter.scalars.ScalarsConverterFactory。 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
Retrofit 至少需要兩項資訊,才能建構網路服務 API:網路服務的基準 URI 和轉換器工廠。轉換工具會向 Retrofit 說明如何處理從 Web 服務傳回的資料。在本例中,您希望 Retrofit 從網路服務擷取 JSON 回應,並以 String 形式傳回。Retrofit 具備支援字串和其他原始類型的 ScalarsConverter,因此您會在具有 ScalarsConverterFactory 例項的建構工具上呼叫 addConverterFactory()。  最後,呼叫 build() 以建立 Retrofit 物件。
- 在對 Retrofit 建構工具的呼叫下方,定義介面,說明 Retrofit 如何使用 HTTP 要求與網路伺服器通訊。依要求匯入 
retrofit2.http.GET和retrofit2.Call。 
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}目前目標是從網路服務取得 JSON 回應字串,您只需要一個方法即可達成:getProperties()。如要告知 Retrofit 這個方法應執行的動作,請使用 @GET 註解,並指定該 Web 服務方法的路徑或端點。在此範例中,端點就是 realestate。叫用 getProperties() 方法時,Retrofit 會將端點 realestate 附加至在 Retrofit 建構工具中定義的基準網址,並建立 Call 物件。該 Call 物件用於啟動要求。
- 在 
MarsApiService介面下方,定義名為MarsApi的公開物件,以初始化 Retrofit 服務。 
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}Retrofit create() 方法會使用 MarsApiService 介面建立 Retrofit 服務本身。由於這項呼叫的代價很高,且應用程式只需要一個 Retrofit 服務執行個體,因此您可以使用名為 MarsApi 的公開物件,向應用程式的其餘部分公開服務,並在該處延遲初始化 Retrofit 服務。現在所有設定都已完成,應用程式每次呼叫 MarsApi.retrofitService 時,都會取得實作 MarsApiService 的單例模式 Retrofit 物件。
步驟 3:在 OverviewViewModel 中呼叫網路服務
- 開啟 
app/java/overview/OverviewViewModel.kt。向下捲動至getMarsRealEstateProperties()方法。 
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}您將在這個方法中呼叫 Retrofit 服務,並處理傳回的 JSON 字串。目前回應只有預留位置字串。
- 刪除將回應設為「Set the Mars API Response here!」的預留位置行。
 - 在 
getMarsRealEstateProperties()中新增下列程式碼。依要求匯入retrofit2.Callback和com.example.android.marsrealestate.network.MarsApi。MarsApi.retrofitService.getProperties()方法會傳回Call物件。接著,您可以在該物件上呼叫enqueue(),在背景執行緒上啟動網路要求。 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})- 按一下以紅色底線標示的 
object字詞。依序選取「Code」>「Implement methods」。從清單中選取onResponse()和onFailure()。
Android Studio 會在每個方法中加入含有 TODO 的程式碼: 
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}- 在 
onFailure()中刪除 TODO,然後將_response設為失敗訊息,如下所示。_response是LiveData字串,可決定要在文字檢視區塊中顯示的內容。每個狀態都需要更新_responseLiveData。
如果網路服務回應失敗,系統會呼叫onFailure()回呼。針對這項回應,請將_response狀態設為"Failure: ",並與Throwable引數中的訊息串連。 
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}- 在 
onResponse()中,刪除 TODO 並將_response設為回應內文。要求成功且網路服務傳回回應時,系統會呼叫onResponse()回呼。 
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}步驟 4:定義網際網路權限
- 編譯並執行 MarsRealEstate 應用程式。請注意,應用程式會立即關閉並顯示錯誤。
   - 按一下 Android Studio 中的「Logcat」分頁標籤,然後記下記錄中的錯誤 (開頭為「」類似的一行)
 
Process: com.example.android.marsrealestate, PID: 10646 java.lang.SecurityException: Permission denied (missing INTERNET permission?)
錯誤訊息指出應用程式可能缺少 INTERNET 權限。連線至網際網路後會引發安全性疑慮,因此根據預設,應用程式無網際網路連線。您必須明確告知 Android 應用程式需要存取網際網路。
- 開啟 
app/manifests/AndroidManifest.xml。在<application>標記前方加上這一行: 
<uses-permission android:name="android.permission.INTERNET" />- 再次編譯並執行應用程式。如果網路連線一切正常,您會看到內含火星物業資料的 JSON 文字。

 - 輕觸裝置或模擬器中的「Back」按鈕,關閉應用程式。
 - 將裝置或模擬器設為飛航模式,然後從「最近用過的」選單重新開啟應用程式,或從 Android Studio 重新啟動應用程式。
 

- 再次關閉飛航模式。
 
您現可從「火星」網路服務取得 JSON 回應,這是個不錯的起點。但您真正需要的是 Kotlin 物件,而非大型 JSON 字串。此外還有一個名為 Moshi 的程式庫,這個 Android JSON 剖析器可將 JSON 字串轉換為 Kotlin 物件。Retrofit 具備可與 Moshi 搭配使用的轉換工具,是非常適合在這裡使用的優異程式庫。
在這項工作中,您會使用 Moshi 程式庫搭配 Retrofit,將網路服務中的 JSON 回應剖析為實用的 Mars Property Kotlin 物件。應用程式會改為顯示傳回的火星房地產數量,而非顯示原始 JSON。
步驟 1:新增 Moshi 程式庫依附元件
- 開啟「build.gradle (Module: app)」。
 - 在依附元件區段新增以下程式碼,以包含 Moshi 依附元件。與 Retrofit 相同,
$version_moshi是在專案層級的 Gradle 檔案中個別定義。這些依附元件會新增核心 Moshi JSON 程式庫的支援,以及 Moshi 的 Kotlin 支援。 
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"- 在 
dependencies區塊中找出 Retrofit 純量轉換工具行: 
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"- 將該行變更為使用 
converter-moshi: 
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"- 按一下「Sync Now」,使用新依附元件重新建構專案。
 
步驟 2:實作 MarsProperty 資料類別
從網路服務取得的 JSON 回應範例項目如下所示:
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]如上所示,JSON 回應為陣列,以方括號表示。陣列包含 JSON 物件,這些物件會以大括號括住。每個物件都包含一組名稱/值配對,並以半形冒號分隔。名稱會以引號括住。值可以是數字或字串,字串則以引號括住。舉例來說,這個屬性的 price 是 $450,000,而 img_src 則是網址,也就是伺服器上圖片檔案的位置。
在上述範例中,請注意每個「火星」屬性項目皆具有以下的 JSON 鍵與值配對:
price:火星房地產的價格,以數字表示。id:屬性的 ID,以字串表示。type:"rent"或"buy"。img_src:網址圖片,以字串表示。
Moshi 會剖析此 JSON 資料,然後將其轉換為 Kotlin 物件。如要這麼做,它必須具有 Kotlin 資料類別以儲存剖析結果,因此下一個步驟是建立該類別。
- 開啟 
app/java/network/MarsProperty.kt。 - 將現有的 
MarsProperty類別定義替換為下列程式碼: 
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)請注意,MarsProperty 類別中的每個變數皆會對應至 JSON 物件中的鍵名。如要比對 JSON 中的類型,請為所有值使用 String 物件,但 price 除外,因為 price 是 Double。Double 可用於表示任何 JSON 數字。
Moshi 剖析 JSON 時,會根據名稱比對鍵,並在資料物件中填入適當的值。
- 將 
img_src鍵這行替換為以下顯示的行。依要求匯入com.squareup.moshi.Json。 
@Json(name = "img_src") val imgSrcUrl: String,有時,JSON 回應中的鍵名可能導致 Kotlin 屬性有所混淆,或與您的程式設計樣式不符;舉例來說,在 JSON 檔案中,img_src 鍵會使用底線,而 Kotlin 屬性通常會使用大小寫字母 (「駝峰式大小寫」)。
如要在資料類別中使用與 JSON 回應中鍵名不同的變數名稱,請使用 @Json 註解。在此範例中,資料類別中的變數名稱為 imgSrcUrl。變數會使用 @Json(name = "img_src") 對應至 JSON 屬性 img_src。
步驟 3:更新 MarsApiService 和 OverviewViewModel
有了 MarsProperty 資料類別,您現在可以更新網路 API 和 ViewModel,加入 Moshi 資料。
- 開啟 
network/MarsApiService.kt。您可能會看到ScalarsConverterFactory的類別遺失錯誤。這是因為您在步驟 1 中已變更 Retrofit 依附元件。您很快就會修正這些錯誤。 - 在檔案頂端的 Retrofit 建構工具之前,新增下列程式碼以建立 Moshi 執行個體。依要求匯入 
com.squareup.moshi.Moshi和com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory。 
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()與 Retrofit 的做法類似,您會使用 Moshi 建構工具建立 moshi 物件。如要讓 Moshi 註解與 Kotlin 順利搭配運作,請新增 KotlinJsonAdapterFactory,然後呼叫 build()。
- 將 Retrofit 建構工具改為使用 
MoshiConverterFactory,而非ScalarConverterFactory,並傳入您剛建立的moshi例項。依要求匯入retrofit2.converter.moshi.MoshiConverterFactory。 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()- 請一併刪除 
ScalarConverterFactory的匯入內容。 
要刪除的程式碼:
import retrofit2.converter.scalars.ScalarsConverterFactory- 更新 
MarsApiService介面,讓 Retrofit 傳回MarsProperty物件清單,而非傳回Call<String>。 
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}- 開啟 
OverviewViewModel.kt。向下捲動至getMarsRealEstateProperties()方法中的getProperties().enqueue()呼叫。 - 將引數從 
Callback<String>變更為Callback<List<MarsProperty>>。enqueue()依要求匯入com.example.android.marsrealestate.network.MarsProperty。 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {- 在 
onFailure()中,將引數從Call<String>變更為Call<List<MarsProperty>>: 
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {- 對 
onResponse()的兩個引數進行相同的變更: 
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {- 在 
onResponse()的主體中,將現有指派給_response.value的指派項目替換為以下顯示的指派項目。由於response.body()現在是MarsProperty物件的清單,因此該清單的大小就是剖析的屬性數量。這則回覆訊息會列印該屬性的數量: 
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"- 確認飛航模式已關閉。編譯並執行應用程式。此時訊息應會顯示從網路服務傳回的屬性數量:

 
現在 Retrofit API 服務正在執行,但它使用回呼和您必須實作的兩個回呼方法。一個方法會處理成功情況,另一個方法則會處理失敗情況,而失敗結果會回報例外狀況。如果能搭配例外狀況處理使用協同程式,而非使用回呼,程式碼會更有效率,也更容易閱讀。方便的是,Retrofit 具有整合協同程式的程式庫。
在這項工作中,您會轉換網路服務和 ViewModel,以使用協同程式。
步驟 1:新增協同程式依附元件
- 開啟「build.gradle (Module: app)」。
 - 在依附元件區段中,新增對 Kotlin 協同程式核心程式庫和 Retrofit 協同程式程式庫的支援:
 
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines" implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
- 按一下「Sync Now」,使用新依附元件重新建構專案。
 
步驟 2:更新 MarsApiService 和 OverviewViewModel
- 在 
MarsApiService.kt中,更新 Retrofit 建構工具,改為使用CoroutineCallAdapterFactory。現在完整的建構工具如下所示: 
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()呼叫轉接程式可讓 Retrofit 建立傳回預設 Call 類別以外內容的 API。在本例中,CoroutineCallAdapterFactory 可讓我們將 getProperties() 傳回的 Call 物件替換為 Deferred 物件。
- 在 
getProperties()方法中,將Call<List<MarsProperty>>變更為Deferred<List<MarsProperty>>。依要求匯入kotlinx.coroutines.Deferred。完整的getProperties()方法如下所示: 
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>Deferred 介面定義了會傳回結果值 (Deferred 繼承自 Job) 的協同程式工作。Deferred 介面包含名為 await() 的方法,可讓程式碼等待值準備就緒,且不會遭到封鎖,然後傳回該值。
- 開啟 
OverviewViewModel.kt。在init區塊之前,新增協同程式工作: 
private var viewModelJob = Job()- 使用主要調度器為該新工作建立協同程式範圍:
 
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )Dispatchers.Main 調度工具會使用 UI 執行緒執行工作。由於 Retrofit 會在背景執行所有工作,因此沒有理由為範圍使用任何其他執行緒。這樣一來,您就能在取得結果時輕鬆更新 MutableLiveData 的值。
- 刪除 
getMarsRealEstateProperties()內的所有程式碼。您會在這裡使用協同程式,而非呼叫enqueue()和onFailure()與onResponse()回呼。 - 在 
getMarsRealEstateProperties()中啟動協同程式: 
coroutineScope.launch { 
}
如要使用 Retrofit 為網路工作傳回的 Deferred 物件,您必須位於協同程式中,因此請在此啟動您剛建立的協同程式。您仍在主執行緒上執行程式碼,但現在要讓協同程式管理並行作業。
- 在啟動區塊中,對 
retrofitService物件呼叫getProperties(): 
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()從 MarsApi 服務呼叫 getProperties() 會在背景執行緒建立並啟動網路呼叫,並傳回該工作的 Deferred 物件。
- 同樣在啟動區塊中,新增 
try/catch區塊來處理例外狀況: 
try {
} catch (e: Exception) {
  
}- 在 
try {}區塊中,對Deferred物件呼叫await(): 
var listResult = getPropertiesDeferred.await()對 Deferred 物件呼叫 await() 時,系統會在值就緒時傳回網路呼叫的結果。await() 方法不會遭到封鎖,因此 Mars API 服務會從網路擷取資料,而不會封鎖目前的執行緒。這點非常重要,因為我們位於 UI 執行緒的範圍內。工作完成後,程式碼會從中斷的地方繼續執行。這是在 try {} 內,因此您可以擷取例外狀況。
- 同樣在 
try {}區塊中,於await()方法後方,更新成功回應的回應訊息: 
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"- 在 
catch {}區塊中,處理失敗回應: 
_response.value = "Failure: ${e.message}"
現在,完整的 getMarsRealEstateProperties() 方法大致如下:
private fun getMarsRealEstateProperties() {
   coroutineScope.launch {
       var getPropertiesDeferred = 
          MarsApi.retrofitService.getProperties()
       try {          
           _response.value = 
              "Success: ${listResult.size} Mars properties retrieved"
       } catch (e: Exception) {
           _response.value = "Failure: ${e.message}"
       }
   }
}- 在類別底部,使用下列程式碼新增 
onCleared()回呼: 
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}ViewModel 遭到毀損時,資料載入作業應停止,因為使用這個 ViewModel 的 OverviewFragment 會消失。如要在 ViewModel 遭到終止時停止載入,請覆寫 onCleared() 來取消工作。
- 編譯並執行應用程式。這次的結果與上一個工作相同 (屬性數量報告),但程式碼和錯誤處理方式更直接。
 
Android Studio 專案:MarsRealEstateNetwork
REST 網路服務
- 「網路服務」是網際網路上的服務,可讓應用程式發出要求和取回資料。
 - 一般的網路服務使用 REST 架構。提供 REST 架構的網路服務稱為「符合 REST 樣式」的服務。符合 REST 樣式的網路服務,均使用標準網路元件和通訊協定建構而成。
 - 透過 URI 以標準化方式向 REST 網路服務傳送要求。
 - 如要使用網路服務,應用程式必須建立網路連線,並與服務通訊。接著,應用程式必須接收回應資料,並將其剖析為可供應用程式使用的格式。
 - Retrofit 程式庫是一個用戶端程式庫,可讓應用程式向 REST Web 服務發出要求。
 - 使用轉換工具告知 Retrofit 該如何處理傳送至網路服務的資料,以及從網路服務傳回的資料。舉例來說,
ScalarsConverter轉換工具會將網路服務資料視為String或其他原始檔案。 - 如要讓應用程式連上網際網路,請在 Android 資訊清單中新增 
"android.permission.INTERNET"權限。 
JSON 剖析
- 網路服務的回應通常會採用 JSON 格式,這是一種代表結構化資料的常用交換格式。
 - JSON 物件是一組鍵/值組合。這個集合有時也稱為「字典」、「雜湊對應」或「關聯陣列」。
 - 一組 JSON 物件稱為 JSON 陣列。您可以從 Web 服務取得 JSON 陣列做為回應。
 - 鍵/值組合的鍵前後會加上半形引號。值可以是數字或字串。字串也會以引號括住。
 - Moshi 程式庫為 Android JSON 剖析器,可將 JSON 字串轉換為 Kotlin 物件。Retrofit 具備可與 Moshi 搭配使用的轉換工具。
 - Moshi 會以同名資料物件的屬性,比對 JSON 回應中的鍵。
 - 如要為某個鍵使用不同的屬性名稱,請為該屬性加上 
@Json註解和 JSON 鍵名。 
Retrofit 和協同程式
- 透過呼叫介面卡,Retrofit 可建立傳回預設 
Call類別以外內容的 API。使用CoroutineCallAdapterFactory類別將Call替換為協同程式Deferred。 - 在 
Deferred物件上使用await()方法,讓協同程式程式碼等待值準備就緒 (不會遭到封鎖),然後傳回該值。 
Udacity 課程:
Android 開發人員說明文件:
Kotlin 說明文件:
其他:
本節列出的作業可由課程講師指派給學習本程式碼研究室的學員。講師可自由採取以下行動:
- 視需要指派作業。
 - 告知學員如何繳交作業。
 - 為作業評分。
 
講師可以視需求使用全部或部分建議內容,也可以自由指派任何其他合適的作業。
如果您是自行學習本程式碼研究室,不妨利用這些作業驗收學習成果。
回答以下問題
第 1 題
在建構 Web 服務 API 時,Retrofit 必須考量哪兩項重點?
▢ 網路服務的基準 URI 和 GET 查詢。
▢ 網路服務的基準 URI 和轉換器工廠。
▢ 網路服務的網路連線,以及授權權杖。
▢ 轉換器工廠和回應剖析器。
第 2 題
Moshi 程式庫有什麼用途?
▢ 從網路服務中取回資料。
▢ 與 Retrofit 互動,產生網路服務要求。
▢ 將網路服務的 JSON 回應剖析為 Kotlin 資料物件。
▢ 重新命名 Kotlin 物件以符合 JSON 回應中的金鑰。
第 3 題
Retrofit 呼叫轉換器有哪些用途?
▢ 可讓 Retrofit 使用協同程式。
▢ 可將網路服務回應轉換成 Kotlin 資料物件。
▢ 可將 Retrofit 呼叫變更為網路服務呼叫。
▢ 可加入傳回 Retrofit 預設 Call 類別以外項目的功能。
開始下一個課程:
如要查看本課程其他程式碼研究室的連結,請參閱 Android Kotlin 基礎知識程式碼研究室登陸頁面。