Android(Java)饮食追踪App如何永久保存MealModel列表数据
实现MealModel列表永久存储的方案
前提准备:让MealModel支持序列化
无论选择哪种存储方案,都需要让MealModel类支持对象序列化,才能将对象转换为可存储的格式。
方式1:实现Serializable接口(简单易用)
修改MealModel类,实现Serializable接口:
import java.io.Serializable; /** * This class holds the data to represent one meal item */ public class MealModel implements Serializable { String mealtype; String healthyness; String date; String pathtoimage; // path in phone gallery boolean favorised; public MealModel(String mealtype, String healthyness, String date, boolean favorised, String pathtoimage) { this.mealtype = mealtype; this.healthyness = healthyness; this.date = date; this.favorised = favorised; this.pathtoimage = pathtoimage; } public String getMealtype() { return mealtype; } public String getHealthyness() { return healthyness; } public String getPathtoimage() { return pathtoimage; } public boolean getFavorised() { return favorised; } public String getDate() { return date; } }
方式2:实现Parcelable接口(Android推荐,效率更高)
如果需要跨组件传递对象,或者追求更高序列化效率,推荐实现Parcelable接口:
import android.os.Parcel; import android.os.Parcelable; public class MealModel implements Parcelable { String mealtype; String healthyness; String date; String pathtoimage; boolean favorised; public MealModel(String mealtype, String healthyness, String date, boolean favorised, String pathtoimage) { this.mealtype = mealtype; this.healthyness = healthyness; this.date = date; this.favorised = favorised; this.pathtoimage = pathtoimage; } protected MealModel(Parcel in) { mealtype = in.readString(); healthyness = in.readString(); date = in.readString(); pathtoimage = in.readString(); favorised = in.readByte() != 0; } public static final Creator<MealModel> CREATOR = new Creator<MealModel>() { @Override public MealModel createFromParcel(Parcel in) { return new MealModel(in); } @Override public MealModel[] newArray(int size) { return new MealModel[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mealtype); dest.writeString(healthyness); dest.writeString(date); dest.writeString(pathtoimage); dest.writeByte((byte) (favorised ? 1 : 0)); } // getter方法保持不变 public String getMealtype() { return mealtype; } public String getHealthyness() { return healthyness; } public String getPathtoimage() { return pathtoimage; } public boolean getFavorised() { return favorised; } public String getDate() { return date; } }
方案1:使用SharedPreferences(适合少量餐食数据)
如果你的应用餐食数量不多(几十条以内),SharedPreferences结合Gson序列化是最简单的方案:
1. 添加Gson依赖
在模块级build.gradle的dependencies中添加:
implementation 'com.google.code.gson:gson:2.10.1'
2. 保存列表到SharedPreferences
// 假设已收集数据并创建MealModel列表 List<MealModel> mealList = new ArrayList<>(); mealList.add(new MealModel("早餐", "健康", "2024-05-20", false, "/storage/emulated/0/Pictures/meal1.jpg")); // 将列表转为JSON字符串 Gson gson = new Gson(); String mealListJson = gson.toJson(mealList); // 写入SharedPreferences SharedPreferences prefs = getContext().getSharedPreferences("MealTrackerPrefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("meal_list", mealListJson); editor.apply();
3. 从SharedPreferences读取列表
SharedPreferences prefs = getContext().getSharedPreferences("MealTrackerPrefs", Context.MODE_PRIVATE); String mealListJson = prefs.getString("meal_list", "[]"); Gson gson = new Gson(); Type type = new TypeToken<List<MealModel>>() {}.getType(); List<MealModel> mealList = gson.fromJson(mealListJson, type); // 用该列表初始化RecyclerView适配器
方案2:使用Room数据库(推荐,适合大量/结构化数据)
如果需要对餐食数据做查询、排序,或者数据量较大,Room是Android官方推荐的本地结构化存储方案:
1. 添加Room依赖
在模块级build.gradle的dependencies中添加:
def room_version = "2.6.1" implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
2. 修改MealModel为Room实体类
添加Room注解,将类映射为数据库表:
import androidx.room.Entity; import androidx.room.PrimaryKey; @Entity(tableName = "meals") public class MealModel { @PrimaryKey(autoGenerate = true) private int id; // 新增主键,用于唯一标识每条数据 String mealtype; String healthyness; String date; String pathtoimage; boolean favorised; // 构造方法去掉id(由Room自动生成) public MealModel(String mealtype, String healthyness, String date, boolean favorised, String pathtoimage) { this.mealtype = mealtype; this.healthyness = healthyness; this.date = date; this.favorised = favorised; this.pathtoimage = pathtoimage; } // 新增id的getter和setter public int getId() { return id; } public void setId(int id) { this.id = id; } // 其他getter方法保持不变 public String getMealtype() { return mealtype; } public String getHealthyness() { return healthyness; } public String getPathtoimage() { return pathtoimage; } public boolean getFavorised() { return favorised; } public String getDate() { return date; } }
3. 创建Room Dao接口
定义数据库操作方法:
import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import java.util.List; @Dao public interface MealDao { // 插入单条餐食 @Insert void insertMeal(MealModel meal); // 插入多条餐食 @Insert void insertAllMeals(List<MealModel> meals); // 查询所有餐食 @Query("SELECT * FROM meals") List<MealModel> getAllMeals(); // 按日期倒序查询餐食 @Query("SELECT * FROM meals ORDER BY date DESC") List<MealModel> getMealsSortedByDate(); }
4. 创建Room数据库类
用单例模式避免重复创建数据库实例:
import androidx.room.Database; import androidx.room.RoomDatabase; @Database(entities = {MealModel.class}, version = 1) public abstract class MealDatabase extends RoomDatabase { public abstract MealDao mealDao(); private static volatile MealDatabase INSTANCE; public static MealDatabase getDatabase(final Context context) { if (INSTANCE == null) { synchronized (MealDatabase.class) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), MealDatabase.class, "meal_database") .build(); } } } return INSTANCE; } }
5. 保存餐食到Room数据库
注意:Room操作不能在主线程执行,推荐用Coroutine处理:
saveBtn.setOnClickListener(view12 -> { // 从控件收集数据 String mealType = view.findViewById(R.id.mealType_textView).getEditableText().toString(); String healthyness = "..."; // 从对应控件获取 String date = "..."; boolean favorised = false; // 根据开关状态获取 String imagePath = "..."; // 图片本地路径 MealModel newMeal = new MealModel(mealType, healthyness, date, favorised, imagePath); // 用Coroutine在IO线程执行数据库操作 CoroutineScope scope = CoroutineScope(Dispatchers.IO); scope.launch { MealDatabase db = MealDatabase.getDatabase(getContext()); db.mealDao().insertMeal(newMeal); // 切换回主线程提示保存成功 withContext(Dispatchers.Main) { Toast.makeText(getContext(), "餐食已保存", Toast.LENGTH_SHORT).show(); } } });
6. 从Room读取餐食列表
// 在Fragment初始化时读取数据 CoroutineScope scope = CoroutineScope(Dispatchers.IO); scope.launch { MealDatabase db = MealDatabase.getDatabase(getContext()); List<MealModel> mealList = db.mealDao().getAllMeals(); // 切换到主线程更新RecyclerView withContext(Dispatchers.Main) { MealAdapter adapter = new MealAdapter(mealList); recyclerView.setAdapter(adapter); } }
方案3:本地文件存储(自定义JSON文件)
也可以将列表序列化为JSON字符串,保存到应用内部存储的文件中:
保存列表到文件
private void saveMealListToFile(List<MealModel> mealList) { try { FileOutputStream fos = getContext().openFileOutput("meals.json", Context.MODE_PRIVATE); Gson gson = new Gson(); String json = gson.toJson(mealList); fos.write(json.getBytes()); fos.close(); } catch (IOException e) { e.printStackTrace(); } }
从文件读取列表
private List<MealModel> loadMealListFromFile() { List<MealModel> mealList = new ArrayList<>(); try { FileInputStream fis = getContext().openFileInput("meals.json"); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line); } fis.close(); Gson gson = new Gson(); Type type = new TypeToken<List<MealModel>>() {}.getType(); mealList = gson.fromJson(sb.toString(), type); } catch (IOException e) { e.printStackTrace(); } return mealList; }
选择建议
- 数据量小(几十条以内):优先用SharedPreferences,实现简单
- 需要结构化存储、查询排序或数据量大:选Room数据库,长期维护更方便
- 自定义文件存储适合需要完全控制存储逻辑的场景,但需自行处理异常和版本兼容
内容的提问来源于stack exchange,提问作者J_R
相关产品推荐
相关产品推荐

