# Send a simple GET Request ![image](https://hackmd.io/_uploads/BJAU1TYj0.png) ### 簡介 - 本影片是 Retrofit 教學系列的開端,目的是展示如何發送 GET 請求並接收 JSON 格式的回應。 - 將使用一個 REST API 伺服器來發送 GET 請求至 `/posts/1` 端點,該端點會回傳一個包含 `userId`、`id`、`title` 和 `body` 四個欄位的 JSON 資料。 ### 專案設置 - 已在 Android Studio 專案中添加必要的依賴,包括 Retrofit、JSON 轉換器和 Kotlin 協程。 ![CleanShot 2024-08-26 at 16.41.36](https://hackmd.io/_uploads/HyXAJTKiC.png) - 使用 `ViewModelScope` 管理協程。 ### 建立 Model - 創建一個名為 `model` 的 package。 - 在 `model` package 中創建一個名為 `Post` 的 data class。 - `Post` class 包含四個欄位:`userId`、`id`、`title` 和 `body`,其中 `userId` 和 `id` 為 `Int`,`title` 和 `body` 為 `String`。 ![CleanShot 2024-08-26 at 16.42.31](https://hackmd.io/_uploads/HyuZgaYs0.png) ### 建立 API 介面 - 創建一個名為 `api` 的 package。 - 在 `api` package 中創建一個名為 `SimpleApi` 的 interface。 - 使用 `@GET` 註解來標記發送 GET 請求的方法,並指定端點 `/posts/1`。 - 在介面中定義一個 `suspend` 函數 `getPost`,該函數會回傳 `Post` 物件。 ![CleanShot 2024-08-26 at 16.43.08](https://hackmd.io/_uploads/r1jQgptjC.png) ### 建立 Constants 類別 - 創建一個名為 `utils` 的 package。 - 在 `utils` package 中創建一個名為 `Constants` 的 class。 - 在 `Constants` class 中定義 `baseUrl` 常數,用來存放 API 的基礎 URL。 ### 設置 Retrofit 實例 ![CleanShot 2024-08-26 at 16.44.58](https://hackmd.io/_uploads/SyI9epFiR.png) - 在 `api` package 中創建一個名為 `RetrofitInstance` 的 object,用來設置 Retrofit 實例。 - 使用 `Retrofit.Builder` 設置 `baseUrl` 和 `converterFactory`,並創建 `Retrofit` 實例。 - 定義 `api` 變數,透過 Retrofit 的 `create` 方法初始化 `SimpleApi` 介面。 ### 建立 Repository 類別 ![CleanShot 2024-08-26 at 16.45.46](https://hackmd.io/_uploads/ByPpxaKjA.png) - 創建一個名為 `repository` 的 package。 - 在 `repository` package 中創建一個名為 `Repository` 的 class。 - 在 `Repository` class 中定義一個 `suspend` 函數 `getPost`,該函數會調用 `RetrofitInstance.api.getPost` 並回傳結果。 ### 建立 ViewModel ![CleanShot 2024-08-26 at 16.47.04](https://hackmd.io/_uploads/HJSzWpFoC.png) - 創建一個名為 `MainViewModel` 的 class,繼承自 `ViewModel`。 - 在 `MainViewModel` 中定義一個 `getPost` 函數,使用 `ViewModelScope.launch` 來啟動協程,並從 Repository 中取得資料。 - 將取得的資料存放在 `MutableLiveData<Post>` 中,以便後續在 Activity 中觀察。 ### 建立 ViewModelFactory - 創建一個名為 `MainViewModelFactory` 的 class,實作 `ViewModelProvider.Factory`。 - 在 `MainViewModelFactory` 中接收 `Repository` 作為參數,並在 `create` 方法中回傳 `MainViewModel` 實例。 ### 在 MainActivity 中整合 ![CleanShot 2024-08-26 at 16.47.49](https://hackmd.io/_uploads/BJZBbpFo0.png) ![CleanShot 2024-08-26 at 16.49.03](https://hackmd.io/_uploads/r11oZpti0.png) - 在 `MainActivity` 中初始化 `MainViewModel`、`Repository` 和 `MainViewModelFactory`。 - 使用 `viewModel.getPost()` 發送 GET 請求並觀察 `MutableLiveData` 物件,處理回應結果。 - 為 `MainActivity` 添加網路權限,以便能夠成功發送請求。 ### 處理例外狀況 ![CleanShot 2024-08-26 at 16.50.35](https://hackmd.io/_uploads/HJPkf6YjR.png) ![CleanShot 2024-08-26 at 16.50.40](https://hackmd.io/_uploads/S1nkMTFoC.png) ![CleanShot 2024-08-26 at 16.50.45](https://hackmd.io/_uploads/SJegG6Ki0.png) ![CleanShot 2024-08-26 at 16.51.22](https://hackmd.io/_uploads/rktMzTtjR.png) - 當請求失敗時,如遇到 404 錯誤(頁面不存在),使用 `Response` 包裝 `Post` 物件來處理錯誤。 - 在 `MainActivity` 中使用 `response.isSuccessful` 方法檢查請求是否成功,並根據結果更新 UI。 # URL Manipulation with @Path @Query @QueryMap ![image](https://hackmd.io/_uploads/BJAR7aYjA.png) ### 動態端點設置 ![CleanShot 2024-08-26 at 17.05.51](https://hackmd.io/_uploads/Sk5KS6YsA.png) - 新增一個函數,使用 `@GET` 註解來指定動態端點 `/posts/{postId}`。 - 函數參數為 `Int` 型別,並使用 `@Path` 註解來對應 `{postId}`。 ### 更新 Repository 和 ViewModel - 在 `Repository` 中新增函數,接受整數參數,調用 `RetrofitInstance` 發送 GET 請求。 ![CleanShot 2024-08-26 at 17.06.29](https://hackmd.io/_uploads/B1KiB6toC.png) - 在 `ViewModel` 中新增函數,使用 `MutableLiveData` 儲存請求結果並觀察資料變動。 ![CleanShot 2024-08-26 at 17.06.49](https://hackmd.io/_uploads/BJSnHptsR.png) ### 更新 UI 和 MainActivity - 更新 `activity_main.xml`,新增數字輸入框和按鈕。 - 在 `MainActivity` 中為按鈕添加 `onClickListener`,用戶輸入數字後傳遞給 `ViewModel`,並更新 UI。 ![CleanShot 2024-08-26 at 17.07.55](https://hackmd.io/_uploads/HJhxLaYiC.png) ### 使用查詢參數 (Query Parameters) - 新增 API 函數,使用查詢參數發送 GET 請求,回傳多個 `Post` 物件的列表。 - 使用 `@Query` 註解添加查詢參數 `userId`。 ![CleanShot 2024-08-26 at 17.08.44](https://hackmd.io/_uploads/rk0Q86KiA.png) - 在 `Repository` 和 `ViewModel` 中更新函數以處理查詢參數。 ![CleanShot 2024-08-26 at 17.09.25](https://hackmd.io/_uploads/BJBIU6YoC.png) ![CleanShot 2024-08-26 at 17.09.51](https://hackmd.io/_uploads/B15wLpYsR.png) ![CleanShot 2024-08-26 at 17.11.16](https://hackmd.io/_uploads/BkMpU6tjR.png) ### 多重查詢參數與排序 ![CleanShot 2024-08-26 at 17.12.20](https://hackmd.io/_uploads/SJ4bwpYiR.png) ![CleanShot 2024-08-26 at 17.13.04](https://hackmd.io/_uploads/S1TXvats0.png) ![CleanShot 2024-08-26 at 17.13.20](https://hackmd.io/_uploads/Hk2Nw6FjA.png) ![CleanShot 2024-08-26 at 17.13.42](https://hackmd.io/_uploads/ByE8vpYj0.png) - 使用多重查詢參數進行 GET 請求,根據 `id` 欄位進行排序。 - 使用 `@Query` 註解,添加 `sort` 和 `order` 參數控制排序。 ### 使用 QueryMap 簡化查詢參數 ![CleanShot 2024-08-26 at 17.16.30](https://hackmd.io/_uploads/HJ5ldpYiC.png) ![CleanShot 2024-08-26 at 17.16.17](https://hackmd.io/_uploads/ryR1OaFjR.png) ![CleanShot 2024-08-26 at 17.16.47](https://hackmd.io/_uploads/BysZuaKiC.png) - 使用 `@QueryMap` 簡化多重查詢參數處理。 - 在 `SimpleApi` 中新增函數,使用 `@QueryMap` 接收 `Map<String, String>`。 - 在 `Repository` 和 `ViewModel` 中更新函數,在 `MainActivity` 中使用 `HashMap` 組合查詢參數。 ![CleanShot 2024-08-26 at 17.17.13](https://hackmd.io/_uploads/S1_XOTFoC.png) # Display results in a RecyclerView ![image](https://hackmd.io/_uploads/rkxyapFsC.png) ### 新增 RecyclerView 到 Layout - 在 `activity_main.xml` 中新增 `RecyclerView`。 - 設定 `RecyclerView` 的水平和垂直約束。 - 設定 `RecyclerView` 的 ID。 ### 建立 RecyclerView 行佈局 - 創建一個名為 `row_layout.xml` 的佈局檔案。 - 佈局中包含四個 `TextView`,對應於 `Post` 模型的四個欄位:`userId`、`id`、`title` 和 `body`。 - 設定 `TextView` 的約束、邊距、ID,以及根佈局的 `padding`。 ### 建立 RecyclerView Adapter - 在 `adapter` package 中創建一個 Kotlin class 名為 `MyAdapter`。 - 在 `MyAdapter` 中設定 `Post` 列表的變數 `myList`。 - 覆寫 `getItemCount` 函數以回傳列表大小。 - 在 `onBindViewHolder` 函數中,將資料動態設置到 `TextView`。 - 建立 `setData` 函數,用於更新資料列表並通知 `RecyclerView`。 ### 更新 MainActivity - 移除 `MainActivity` 中除了 `Repository`、`ViewModel` 和 `ViewModelFactory` 之外的所有程式碼。 - 觀察 `LiveData`,並在資料變更時更新 `RecyclerView`。 - 懶加載 `RecyclerView Adapter`,並建立 `setupRecyclerView` 函數,配置 `RecyclerView` 的 `Adapter` 和 `LayoutManager`。 - 在 `ViewModel` 中調用 `getCustomPosts` 函數,硬編碼 `userId`、`sort` 和 `order` 參數。 - 檢查 API 回應是否成功,成功則將資料傳遞給 `RecyclerView Adapter`;否則顯示錯誤訊息。 ### 測試應用程式 - 執行應用程式並確認 `RecyclerView` 正確顯示 `Post` 列表。 - 測試不同的 `userId` 參數,並確認 `RecyclerView` 的資料按照預期順序顯示。 # Send a simple POST Request ![image](https://hackmd.io/_uploads/ByklpaYsR.png) ### 發送 POST 請求 - 本次影片展示如何使用 Retrofit 發送 POST 請求。 - 使用 POST 請求將資料發送到伺服器,並在回應中接收與發送相同的資料。 ### 設定 POST 請求 ![CleanShot 2024-08-26 at 17.41.35](https://hackmd.io/_uploads/ByaAppKjA.png) - 在 `SimpleApi` 介面中新增一個 `pushPost` 函數,並使用 `@POST` 註解。 - 函數接受一個 `Post` 類別物件作為參數,並使用 `@Body` 註解,將資料放入請求的 body。 - 函數回傳一個包裹在 `Response` 中的 `Post` 物件。 ### 更新 Repository 和 ViewModel ![CleanShot 2024-08-26 at 17.42.03](https://hackmd.io/_uploads/rJUlCTYsA.png) ![CleanShot 2024-08-26 at 17.42.17](https://hackmd.io/_uploads/H1UZApYsA.png) - 在 `Repository` 中新增 `pushPost` 函數,接收一個 `Post` 物件並發送 POST 請求。 - 在 `ViewModel` 中新增對應的函數,使用 Kotlin 協程或 `viewModelScope` 來調用 `pushPost`,並將回應結果儲存在 `MutableLiveData` 中。 ### 使用表單 URL 編碼發送 POST 請求 ![CleanShot 2024-08-26 at 17.43.21](https://hackmd.io/_uploads/S1yIRpKjR.png) - 新增一個 `pushPost2` 函數,使用 `@FormUrlEncoded` 註解。 - `@Field` 註解用於指定 key-value 格式的資料,並將資料動態傳遞給伺服器。 - ![CleanShot 2024-08-26 at 17.45.56](https://hackmd.io/_uploads/B1QkJAKiA.png) ![CleanShot 2024-08-26 at 17.46.26](https://hackmd.io/_uploads/SkkWJRKiC.png) - 在 `Repository` 和 `ViewModel` 中新增對應的函數來處理這些參數。 ### 測試與分析 ![CleanShot 2024-08-26 at 17.43.38](https://hackmd.io/_uploads/BkUURTtjR.png) - 使用 Wireshark 分析網路流量,檢視發送的請求和伺服器回應。 - 比較 JSON 格式與表單 URL 編碼的請求,檢查請求的 content type 和資料格式。 - 觀察回應中的資料,確保請求成功。 # Custom Request Headers ![image](https://hackmd.io/_uploads/rJ5dJRtoR.png) ## Intro ![CleanShot 2024-08-26 at 17.49.25](https://hackmd.io/_uploads/HkF3kAto0.png) ![CleanShot 2024-08-26 at 17.49.43](https://hackmd.io/_uploads/ByfTJAFo0.png) ![CleanShot 2024-08-26 at 17.49.52](https://hackmd.io/_uploads/Bksa1AFjA.png) ![CleanShot 2024-08-26 at 17.49.58](https://hackmd.io/_uploads/rydRk0to0.png) # Interceptor ![CleanShot 2024-08-26 at 17.51.12](https://hackmd.io/_uploads/rJRzxCFj0.png) # Client ![CleanShot 2024-08-26 at 17.51.35](https://hackmd.io/_uploads/B1O4gRFo0.png) # Wireshark ![CleanShot 2024-08-26 at 17.54.48](https://hackmd.io/_uploads/Sk9gWRKoA.png) # Header ![CleanShot 2024-08-26 at 17.55.11](https://hackmd.io/_uploads/rJCWZAtiR.png) # Terminology - Retrofit:一個用於 Android 的類型安全 HTTP 客戶端庫,簡化了網絡請求的處理。 - GET Request:HTTP 請求的一種方法,用於從伺服器獲取數據。 - JSON:JavaScript Object Notation,一種輕量級的數據交換格式。 - ViewModel:Android 的架構組件之一,用於管理 UI 相關的數據,使數據能夠在設備旋轉等配置變更後保持不變。 - ViewModelScope:一個擁有 ViewModel 生命週期的協程範圍,用於在 ViewModel 中執行協程操作。 - Data Class:在 Kotlin 中用於保存數據的類型,提供了自動生成的 `equals()`、`hashCode()` 和 `toString()` 方法。 - @SerializedName:Gson 序列化注解,用於指定 JSON 字段與 Kotlin 變量之間的映射。 - Interface:一個定義方法但不實現它們的藍圖,用於規範不同類型之間的通信。 - Suspend Function:Kotlin 中的協程函數,用於在非阻塞的方式下執行耗時操作。 - MutableLiveData:Android 架構中的可變觀察數據持有者,用於存儲和觀察 UI 數據。 - Retrofit Builder:用於構建 Retrofit 實例的設計模式,允許配置 base URL、Converter Factory 等。 - Converter Factory:用於在 Retrofit 中將 HTTP 回應轉換為所需類型的工廠,常見的是 GsonConverterFactory。 - Singleton:設計模式中的單例模式,確保一個類別只有一個實例。 - Repository:一種架構模式,用於抽象數據訪問層,管理來自多個數據源的數據操作。 - ViewModelFactory:用於創建 ViewModel 的工廠模式,允許傳遞參數到 ViewModel。 - Coroutine:Kotlin 中用於異步編程的輕量級線程,可以簡化異步代碼。 - Response:Retrofit 的一個封裝類,用於包裹 HTTP 回應及其狀態。 - HTTP 404:HTTP 狀態碼,表示所請求的頁面不存在。 - Logcat:Android Studio 中的一個工具,用於顯示設備的日誌訊息,有助於調試。 - TextView:Android 的一種 UI 元素,用於顯示文本。 - Retrofit:一個用於 Android 的類型安全 HTTP 客戶端庫,簡化了網絡請求的處理。 - GET Request:HTTP 請求的一種方法,用於從伺服器獲取數據。 - JSON:JavaScript Object Notation,一種輕量級的數據交換格式。 - ViewModel:Android 的架構組件之一,用於管理 UI 相關的數據,使數據能夠在設備旋轉等配置變更後保持不變。 - ViewModelScope:一個擁有 ViewModel 生命週期的協程範圍,用於在 ViewModel 中執行協程操作。 - Data Class:在 Kotlin 中用於保存數據的類型,提供了自動生成的 `equals()`、`hashCode()` 和 `toString()` 方法。 - @SerializedName:Gson 序列化注解,用於指定 JSON 字段與 Kotlin 變量之間的映射。 - Interface:一個定義方法但不實現它們的藍圖,用於規範不同類型之間的通信。 - Suspend Function:Kotlin 中的協程函數,用於在非阻塞的方式下執行耗時操作。 - MutableLiveData:Android 架構中的可變觀察數據持有者,用於存儲和觀察 UI 數據。 - Retrofit Builder:用於構建 Retrofit 實例的設計模式,允許配置 base URL、Converter Factory 等。 - Converter Factory:用於在 Retrofit 中將 HTTP 回應轉換為所需類型的工廠,常見的是 GsonConverterFactory。 - Singleton:設計模式中的單例模式,確保一個類別只有一個實例。 - Repository:一種架構模式,用於抽象數據訪問層,管理來自多個數據源的數據操作。 - ViewModelFactory:用於創建 ViewModel 的工廠模式,允許傳遞參數到 ViewModel。 - Coroutine:Kotlin 中用於異步編程的輕量級線程,可以簡化異步代碼。 - Response:Retrofit 的一個封裝類,用於包裹 HTTP 回應及其狀態。 - HTTP 404:HTTP 狀態碼,表示所請求的頁面不存在。 - Logcat:Android Studio 中的一個工具,用於顯示設備的日誌訊息,有助於調試。 - TextView:Android 的一種 UI 元素,用於顯示文本。 - @Path:在 Retrofit 中用於將方法參數映射到 URL 路徑的注解。 - EditText:Android 中的 UI 元素,允許用戶輸入和編輯文本。 - OnClickListener:Android 中的介面,用於處理按鈕等 UI 元素的點擊事件。 - @Query:在 Retrofit 中用於將方法參數映射到 URL 查詢參數的注解。 - HashMap:一種鍵值對集合,用於快速查找和存儲數據。 - RecyclerView:Android 中的一種高效的視圖,用於顯示大量數據集合。 - Descending Order:從高到低的排序順序,通常用於數據列表的排序。 - Ascending Order:從低到高的排序順序,通常用於數據列表的排序。 - MutableLiveData Object:一種可以改變和觀察的數據容器,用於 ViewModel 中的數據更新和傳遞。 - Custom Query:在 URL 中附加自定義查詢參數,用於根據特定條件過濾或排序數據。 - Query Map:在 Retrofit 中使用的注解,允許將多個查詢參數以鍵值對的形式傳遞到 URL。 - For Each:Kotlin 中用於迭代集合的循環結構,允許逐一處理集合中的每個元素。 - TextView.setText():在 Android 中用於設置 TextView 顯示文本的函數。 - Retrofit:一個用於 Android 的類型安全 HTTP 客戶端庫,簡化了網絡請求的處理。 - GET Request:HTTP 請求的一種方法,用於從伺服器獲取數據。 - JSON:JavaScript Object Notation,一種輕量級的數據交換格式。 - RecyclerView:Android 中的一種高效的視圖,用於顯示大量數據集合。 - Row Layout:RecyclerView 中的每個項目佈局,用於顯示單個數據項目。 - TextView:Android 的一種 UI 元素,用於顯示文本。 - Adapter:RecyclerView 中的適配器,用於將數據綁定到視圖。 - ViewHolder:RecyclerView 中的類別,用於存儲和重用視圖的引用,提升性能。 - LayoutManager:RecyclerView 的佈局管理器,用於控制 RecyclerView 中項目的佈局方式。 - LinearLayoutManager:RecyclerView 的一種佈局管理器,將項目線性排列在垂直或水平方向上。 - MutableLiveData:Android 架構中的可變觀察數據持有者,用於存儲和觀察 UI 數據。 - LiveData Observer:一個觀察者,用於監聽 LiveData 的變化並更新 UI。 - notifyDataSetChanged():RecyclerView 中的一個方法,用於通知適配器數據已改變,並刷新視圖。 - onBindViewHolder():RecyclerView 適配器中的方法,用於綁定數據到 ViewHolder。 - setData():自定義的方法,用於將數據傳遞給 RecyclerView 的適配器並更新列表。 - Toast:Android 中的短暫消息顯示方式,用於向用戶顯示簡短的信息。 - onCreate():Android 中的活動生命週期方法,用於初始化活動佈局和數據。 - ViewModel:Android 的架構組件之一,用於管理 UI 相關的數據,使數據能夠在設備旋轉等配置變更後保持不變。 - LinearLayout:Android 中的一種佈局管理方式,將子元素排列成單行或單列。 - Repository:一種架構模式,用於抽象數據訪問層,管理來自多個數據源的數據操作。 - Descending Order:從高到低的排序順序,通常用於數據列表的排序。 - onClickListener:Android 中的介面,用於處理按鈕等 UI 元素的點擊事件。 - Live Template:Android Studio 中的一種自動代碼生成工具,通過模板快速生成代碼塊。 - Retrofit:一個用於 Android 的類型安全 HTTP 客戶端庫,簡化了網絡請求的處理。 - POST Request:HTTP 請求的一種方法,用於向伺服器發送數據,以創建或更新資源。 - Request Body:HTTP 請求中包含的數據部分,與 URL 分開,用於傳遞資料。 - @POST Annotation:Retrofit 中用於標識 POST 請求的方法注解。 - @Body Annotation:Retrofit 中用於將參數設置為 HTTP 請求正文的注解。 - Response:Retrofit 的一個封裝類,用於包裹 HTTP 回應及其狀態。 - Form URL Encoded:一種 MIME 類型,表示請求數據已被編碼為表單的鍵值對格式,數據使用 UTF-8 編碼後,再進行 URI 編碼。 - @FormUrlEncoded Annotation:Retrofit 中用於指示請求應以表單 URL 編碼格式發送的注解。 - @Field Annotation:Retrofit 中用於將參數設置為表單字段的注解。 - Wireshark:一個網絡協議分析工具,用於捕獲和分析網絡數據包。 - Content-Type:HTTP 標頭,用於指定請求或響應中的媒體類型。 - JSON (JavaScript Object Notation):一種輕量級的數據交換格式,易於人閱讀和編寫,也易於機器解析和生成。 - Key-Value Pair:由鍵和值組成的一對數據,通常用於表示表單數據或 JSON 格式的數據。 - UTF-8:一種字符編碼格式,用於將多語言字符轉換為字節。 - URI Encoding:將字符轉換為一個或多個字節的過程,通常在 URL 中使用,以確保 URL 中的字符安全傳輸。 - Application/JSON:一種 MIME 類型,表示請求或響應數據以 JSON 格式傳遞。 - Application/x-www-form-urlencoded:一種 MIME 類型,表示數據以鍵值對格式進行 URL 編碼並傳遞。 - Response Code:HTTP 回應中的狀態碼,用於指示請求的結果,例如 201 表示資源創建成功。 - @FieldMap Annotation:在 Retrofit 中用於將多個字段映射為表單數據的注解。