如何在Room数据库中存储TypeFace对象?解决序列化反序列化崩溃问题
如何在Room数据库中存储Typeface对象?
问题描述
我正在开发一款应用,通过Google Fonts API请求所有字体,使用FontsContractCompat.FontRequestCallback获取Typeface对象并添加到List<TypeFace>中。尝试将Typeface对象序列化为字符串存入Room数据库,但反序列化后调用setTypeface()时应用崩溃,相关代码如下:
Typeface typeface = new Gson().fromJson(item.getTypefaceString(), Typeface.class); mTextName.setTypeface(typeface); // <-- 此处崩溃
核心原因
Typeface是绑定系统字体资源的对象,包含底层Native资源引用,无法通过常规JSON序列化/反序列化完整保留其状态。反序列化得到的Typeface对象是残缺的,没有关联实际字体资源,调用时必然崩溃。
可行解决方案:存储字体元数据而非Typeface对象
不要直接存储Typeface实例,而是存储能重建Typeface的关键信息,需要时再根据这些信息重新加载字体:
1. 定义字体元数据实体类
创建用于Room存储的实体类,保存字体核心信息:
@Entity(tableName = "fonts") public class FontEntity { @PrimaryKey(autoGenerate = true) private long id; // Google Fonts API返回的字体家族名(如"Roboto") private String fontFamily; // 字体样式(对应Typeface.NORMAL/BOLD/ITALIC等) private int fontStyle; // 字体权重(部分Google Fonts字体需要) private int fontWeight; // 本地缓存的字体文件路径(可选,用于离线加载) private String localFilePath; // 构造方法、Getter、Setter省略 }
2. 存储时记录元数据
获取到Typeface后,提取对应元数据存入Room:
// 假设从Google Fonts API获取的字体信息对象为Webfont Webfont webfont = ...; // 从API响应中拿到的字体数据 Typeface typeface = ...; // 已加载的Typeface实例 FontEntity fontEntity = new FontEntity(); fontEntity.setFontFamily(webfont.getFamily()); fontEntity.setFontStyle(typeface.getStyle()); fontEntity.setFontWeight(webfont.getWeight()); // 若已将字体缓存到本地,记录文件路径 fontEntity.setLocalFilePath(getCachedFontPath(webfont.getFamily())); // 插入Room数据库 fontDao.insert(fontEntity);
3. 加载时重建Typeface
从Room读取FontEntity后,根据元数据重新加载字体:
FontEntity fontEntity = fontDao.getFontById(fontId); Typeface typeface; // 优先从本地缓存加载 if (fontEntity.getLocalFilePath() != null && new File(fontEntity.getLocalFilePath()).exists()) { typeface = Typeface.createFromFile(fontEntity.getLocalFilePath()); mTextName.setTypeface(typeface); } else { // 异步请求加载Google Fonts FontRequest request = new FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", fontEntity.getFontFamily(), R.array.com_google_android_gms_fonts_certs ); FontsContractCompat.requestFont(context, request, new FontsContractCompat.FontRequestCallback() { @Override public void onTypefaceRetrieved(Typeface retrievedTypeface) { mTextName.setTypeface(retrievedTypeface); // 可选:将加载后的字体缓存到本地,下次直接从文件加载 cacheFontToLocal(retrievedTypeface, fontEntity.getFontFamily()); } @Override public void onTypefaceRequestFailed(int reason) { // 处理加载失败逻辑(如 fallback 到系统字体) } }, null); }
4. 备选:自定义Gson TypeAdapter(不推荐)
如果一定要尝试序列化,可自定义Gson适配器,但仅对系统自带字体有效,Google Fonts下载的自定义字体无法正确重建:
public class TypefaceAdapter extends TypeAdapter<Typeface> { @Override public void write(JsonWriter out, Typeface value) throws IOException { out.beginObject(); out.name("family").value(value.getFamily()); out.name("style").value(value.getStyle()); out.endObject(); } @Override public Typeface read(JsonReader in) throws IOException { in.beginObject(); String family = null; int style = Typeface.NORMAL; while (in.hasNext()) { switch (in.nextName()) { case "family": family = in.nextString(); break; case "style": style = in.nextInt(); break; } } in.endObject(); return Typeface.create(family, style); } }
使用时注册到Gson:
Gson gson = new GsonBuilder() .registerTypeAdapter(Typeface.class, new TypefaceAdapter()) .create();
内容的提问来源于stack exchange,提问作者Chris P.
相关产品推荐
相关产品推荐

