You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 21:13:05