使用for循环操作SharedPreferences存取值遇问题,求解决方案
Hey,作为刚入门Android开发的新手,你踩的这些坑我当年也都踩过,慢慢来,咱们一步步理清楚。
先说说你用SharedPreferences的问题所在
你原来把StoreItem的每个字段拆分存储到SharedPreferences里,这种做法不仅繁琐,还很容易出问题:
- 存储时先调用了
editor2.apply(),之后又在循环后commit(),虽然这俩方法不冲突,但逻辑混乱,而且如果中途出现异常,可能导致部分数据没写入。 - 读取时依赖
Items Amount这个值,如果这个值没正确写入,整个循环就会出错,直接导致boughtItems为空或者数据不全。 - 崩溃的直接原因是:数据读取失败后,
imageName变成了默认的空格,调用getResourceID时找不到对应的drawable资源,抛出异常,进而在onResume调用showItems时崩溃。
修复SharedPreferences的正确姿势:用JSON序列化
别再拆分字段存储了,直接把整个ArrayList<StoreItem>转成JSON字符串存进去,简洁又不容易出错。你之前说Gson抛安全异常,大概率是Proguard混淆把StoreItem的字段名改了,导致Gson反射失败,加个混淆规则就行。
存储代码
// 存储boughtItems列表 SharedPreferences prefs = getSharedPreferences("BoughtStuff", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); Gson gson = new Gson(); String itemsJson = gson.toJson(boughtItems); editor.putString("saved_bought_items", itemsJson); editor.apply(); // 用apply()异步写入,比commit()更高效
读取代码
// 读取boughtItems列表 SharedPreferences prefs = getSharedPreferences("BoughtStuff", MODE_PRIVATE); String itemsJson = prefs.getString("saved_bought_items", "[]"); // 默认空数组 Gson gson = new Gson(); Type itemListType = new TypeToken<ArrayList<StoreItem>>() {}.getType(); boughtItems = gson.fromJson(itemsJson, itemListType); // 保险起见,空判断避免NullPointerException if (boughtItems == null) { boughtItems = new ArrayList<>(); }
解决Gson的安全异常
在你的Proguard规则文件(proguard-rules.pro)里加上:
# 不让Proguard混淆StoreItem类的字段 -keep class com.example.lorand.saimibutton.StoreItem { *; }
关于Parcelable:别用它做持久化!
你写的StoreArrayHolder其实用错场景了——Parcelable是用来在Activity、Fragment之间传递数据的,比如通过Intent传值,不是用来持久化到磁盘的。如果要让StoreItem支持Parcelable,正确写法应该是让它自己实现Parcelable接口:
public class StoreItem implements Parcelable { public int price; public String name; public String imageName; // 你的其他字段、构造方法和业务方法 // Parcelable相关实现 protected StoreItem(Parcel in) { price = in.readInt(); name = in.readString(); imageName = in.readString(); } public static final Creator<StoreItem> CREATOR = new Creator<StoreItem>() { @Override public StoreItem createFromParcel(Parcel in) { return new StoreItem(in); } @Override public StoreItem[] newArray(int size) { return new StoreItem[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(price); dest.writeString(name); dest.writeString(imageName); } }
如果要通过Intent传递这个列表:
// 发送方 Intent intent = new Intent(this, NextActivity.class); intent.putParcelableArrayListExtra("bought_items", boughtItems); startActivity(intent); // 接收方 ArrayList<StoreItem> receivedItems = getIntent().getParcelableArrayListExtra("bought_items");
给新手的技术选型建议
根据你的场景,按优先级推荐:
- SharedPreferences + JSON:适合存储少量简单数据(比如你的商品列表,数量不多的话),代码简单,容易上手,是入门首选。
- Room数据库:如果以后你的商品数量变多,或者需要做查询、修改单个商品这类操作,Room是Android官方推荐的本地数据库方案,基于SQLite封装,API友好,还支持LiveData等组件,适合复杂数据持久化。
- Parcelable:只用来做组件间的数据传递,别用来存到本地磁盘。
最后,给你个小建议:在getResourceID方法里加个异常捕获,就算图片找不到也不会崩溃,而是显示默认图:
public int getResourceID(String resName, String resType, Context context) { try { return context.getResources().getIdentifier(resName, resType, context.getPackageName()); } catch (Exception e) { e.printStackTrace(); // 返回你的默认图片ID return R.drawable.default_item_image; } }
内容的提问来源于stack exchange,提问作者Lorand D Nagy
相关产品推荐
相关产品推荐

