今天的最終挑戰,是要為你的 App 加上一個「記憶力」。Room 資料庫,讓你的 App 即使在沒有網路的情況下,也能顯示資料,這會讓你的專案變得更完整、更專業。
比喻:
要請「專業圖書館管理員」(Room)來幫忙,你需要在專案中簽約。
打開你的 build.gradle (Module: app) 檔案,在 dependencies 區塊中加入這些依賴。
`dependencies {
// ... 其他依賴
// Room Database 函式庫
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// For Kotlin: use kapt instead of annotationProcessor
// kapt "androidx.room:room-compiler:$room_version"
}`
接著,點擊右上角的 "Sync Now",讓 Android Studio 下載並安裝這個函式庫。
這就像是為你的圖書館**「設計書架」**,告訴資料庫你要儲存的資料長什麼樣子。
建立一個新的 Java 類別,取名為 WeatherEntity。
`import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "weather_table")
public class WeatherEntity {
@PrimaryKey(autoGenerate = true)
private int id;
private String cityName;
private String description;
private double temperature;
private long lastUpdated; // 記錄資料更新時間
// Constructor, getters and setters
// 你可以讓 Android Studio 自動生成這些程式碼
}`
@Entity:這個註解告訴 Room,這個類別是資料庫裡的一張「表格」。@PrimaryKey:告訴 Room,id 是這張表格的「主鍵」,每個資料都獨一無二。這是你的**「圖書館借閱櫃檯」**,用來定義如何與資料庫互動。
建立一個新的 Java interface,取名為 WeatherDao。
`import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
@Dao
public interface WeatherDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertWeather(WeatherEntity weather);
@Query("SELECT * FROM weather_table WHERE cityName = :cityName")
WeatherEntity getWeather(String cityName);
}`
@Dao:這個註解告訴 Room,這是一個資料存取介面。@Insert:定義一個「插入」資料的方法。OnConflictStrategy.REPLACE 確保如果資料已存在,就直接覆蓋掉。@Query:這是一個自訂查詢,用來從資料庫中「讀取」指定城市的資料。現在,我們需要為你的 App 「建造這個圖書館」。
建立一個新的 Java abstract class,取名為 AppDatabase,並繼承 RoomDatabase。
`import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(entities = {WeatherEntity.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract WeatherDao weatherDao();
}`
@Database:告訴 Room,這是資料庫的配置。entities 指定要包含哪些表格,version 是資料庫的版本號。abstract 方法:這個方法會回傳你的 WeatherDao 實例,讓你能夠存取資料庫。這是最核心的部分,我們要讓你的 ViewModel 變得更聰明,能夠**「判斷」要從網路還是資料庫取得資料**。
在你的 WeatherViewModel 中,加入資料庫的相關邏輯。
// 在 WeatherViewModel.java 中...
private final WeatherDao weatherDao;
private final AppDatabase db;
public WeatherViewModel(Application application) {
super(application);
// 建立資料庫實例
db = Room.databaseBuilder(
application,
AppDatabase.class, "weather-db"
).allowMainThreadQueries().build(); // 這裡為了方便示範,實際開發不建議在主執行緒查詢
weatherDao = db.weatherDao();
}
public void fetchWeather(String cityName) {
// 1. 先嘗試從資料庫讀取資料
WeatherEntity cachedData = weatherDao.getWeather(cityName);
if (cachedData != null && !isDataExpired(cachedData)) {
// 如果資料存在且未過期,直接顯示
_weatherData.setValue(cachedData.getDescription()); // 假設你只顯示描述
return;
}
// 2. 如果資料不存在或已過期,才從網路抓取
// ... Retrofit 網路請求的程式碼,這裡省略 ...
call.enqueue(new Callback<WeatherResponse>() {
@Override
public void onResponse(...) {
if (response.isSuccessful() && response.body() != null) {
// 3. 網路請求成功,將新資料存入資料庫
WeatherEntity newEntity = new WeatherEntity(...); // 建立新的資料物件
weatherDao.insertWeather(newEntity);
_weatherData.setValue(newEntity.getDescription());
} else {
// ... 錯誤處理 ...
}
}
// ... onFailure() 錯誤處理 ...
});
}
這個範例展示了「離線優先」的思維:先檢查 App 的「個人圖書館」,找不到或太舊了,才打「網路電話」出去詢問。
終於30天結束了,真的鬆了一大口氣,希望自己能更精進自己之後有機會能夠再次參加活動。