如何用Gson将List<View>保存到SharedPreferences?遇栈溢出问题求助
问题原因分析
1. 初始Gson序列化失败的原因
Android的View及其依赖类(如InsetDrawable)存在继承关系,子类与父类定义了同名的mState字段。Gson默认会序列化类的所有字段,包括父类继承的字段,这就导致了字段重复的冲突异常。
2. 栈溢出的原因
即使使用SuperclassExclusionStrategy排除了父类字段,View对象内部存在复杂的循环引用链:比如View持有Context实例,Context又关联着当前Activity,而Activity又持有整个视图树的引用。Gson序列化时会递归遍历所有关联对象,陷入无限递归调用,最终触发栈溢出错误。
更关键的是,View本身包含大量与当前运行环境绑定的状态(如布局参数、绘制缓存、窗口引用等),这些状态无法通过JSON正确序列化和反序列化,强行序列化没有实际意义。
解决方法:序列化View的配置数据而非View本身
正确的做法是提取View中需要保存的关键配置信息,用一个独立的数据类存储,再序列化这个数据类列表。
步骤1:定义View配置数据类
创建一个ViewConfig类,存储你需要保存的View属性,比如:
public class ViewConfig { // View类型,例如"TextView"、"Button" public String viewType; // 位置与尺寸 public int left; public int top; public int width; public int height; // TextView/Button的文本内容 public String text; // 背景色 public int backgroundColor; // 可根据需求添加更多属性,比如文字大小、颜色、字体等 }
步骤2:将View转换为ViewConfig
编写转换方法,把View实例转换成ViewConfig:
private ViewConfig convertViewToConfig(View view) { ViewConfig config = new ViewConfig(); // 获取View类型 config.viewType = view.getClass().getSimpleName(); // 获取位置与尺寸(若View在布局中,也可通过getLayoutParams获取参数) Rect rect = new Rect(); view.getGlobalVisibleRect(rect); config.left = rect.left; config.top = rect.top; config.width = view.getWidth(); config.height = view.getHeight(); // 根据View类型设置特有属性 if (view instanceof TextView) { TextView textView = (TextView) view; config.text = textView.getText().toString(); config.backgroundColor = textView.getSolidColor(); } else if (view instanceof Button) { Button button = (Button) view; config.text = button.getText().toString(); config.backgroundColor = button.getSolidColor(); } // 其他自定义View或系统View类型可在此扩展处理 return config; }
步骤3:保存配置到SharedPreferences
将List<View>转换为List<ViewConfig>后,再用Gson序列化:
@Override protected void onPause() { super.onPause(); List<View> appViews = new ArrayList<>(); // 假设此处已完成appViews列表的填充 List<ViewConfig> configs = new ArrayList<>(); for (View view : appViews) { configs.add(convertViewToConfig(view)); } // 使用普通Gson实例即可,无需特殊配置 Gson gson = new Gson(); String json = gson.toJson(configs); // 保存到SharedPreferences SharedPreferences sp = getSharedPreferences("view_configs", MODE_PRIVATE); sp.edit().putString("saved_views", json).apply(); }
步骤4:从SharedPreferences加载并重建View
读取JSON后转换为List<ViewConfig>,再根据配置重建View:
@Override protected void onResume() { super.onResume(); SharedPreferences sp = getSharedPreferences("view_configs", MODE_PRIVATE); String json = sp.getString("saved_views", null); if (json != null) { Gson gson = new Gson(); Type type = new TypeToken<List<ViewConfig>>(){}.getType(); List<ViewConfig> configs = gson.fromJson(json, type); // 遍历配置重建View并添加到目标布局 for (ViewConfig config : configs) { View view = createViewFromConfig(config); if (view != null) { yourParentLayout.addView(view); } } } } private View createViewFromConfig(ViewConfig config) { View view = null; // 根据viewType创建对应的View实例 switch (config.viewType) { case "TextView": view = new TextView(this); ((TextView) view).setText(config.text); ((TextView) view).setBackgroundColor(config.backgroundColor); break; case "Button": view = new Button(this); ((Button) view).setText(config.text); ((Button) view).setBackgroundColor(config.backgroundColor); break; // 其他View类型可在此扩展 } if (view != null) { // 设置位置与尺寸 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(config.width, config.height); view.setLayoutParams(params); view.setX(config.left); view.setY(config.top); } return view; }
注意事项
- 只序列化你真正需要恢复的属性,避免冗余字段,减少JSON体积和序列化开销。
- 若涉及自定义View的特有配置,需在
ViewConfig中对应添加字段,并在转换方法中补充处理逻辑。 - 绝对不要尝试序列化
Context、ViewGroup等与运行环境强绑定的对象,这类对象无法通过JSON正确恢复。
内容的提问来源于stack exchange,提问作者Babushka
相关产品推荐
相关产品推荐

