优秀博客
Typeface-为自定义字体提供字体内存缓存:https://www.cnblogs.com/yongdaimi/p/8032537.html
Android 系统字体:https://blog.csdn.net/rjdeng/article/details/48545313
浅析Android字体加载原理:https://blog.csdn.net/a282255307/article/details/76870441
字库使用
1.字库组成
字库配置在framework/base/data/fonts/fonts.xml中
<familyset version="22">
<!-- first font is default -->
<family name="sans-serif">
<font weight="100" style="normal">Roboto-Thin.ttf</font>
<font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
<font weight="300" style="normal">Roboto-Light.ttf</font>
<font weight="300" style="italic">Roboto-LightItalic.ttf</font>
<font weight="400" style="normal">Roboto-Regular.ttf</font>
<font weight="400" style="italic">Roboto-Italic.ttf</font>
<font weight="500" style="normal">Roboto-Medium.ttf</font>
<font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
<font weight="900" style="normal">Roboto-Black.ttf</font>
<font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
<font weight="700" style="normal">Roboto-Bold.ttf</font>
<font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
</family>
<!-- Note that aliases must come after the fonts they reference. -->
<alias name="sans-serif-thin" to="sans-serif" weight="100" />
<alias name="sans-serif-light" to="sans-serif" weight="300" />
<alias name="sans-serif-medium" to="sans-serif" weight="500" />
...
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
<font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family lang="und-Arab" variant="compact">
<font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
<font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
</family>
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="elegant">
<font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
</family>
...
</familyset>
基本字库和国家相关字库。国家相关字库在font.xml中的fallback fonts下配置
2.字库功能查询
fallback fonts之上的字库属于基本字库,基本是希腊问、拉丁文等通用文字字库
查询方法一
具体功能可以在字客网: https://www.fontke.com/font/86795/download/ ,使用字库名称查询
字库信息
查询方法二
针对fallback fonts下的字库,有一个简单的方式,如
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
观察多个字库命名方式,可以确定Cherokee是该字库提供的预言。有道查询Cherokee
字客网查询
对于有些缩写的,字典查不到,就在字客网上查询即可。
3. 系统字库使用
由于字库使用频繁且字库可能比较大,开机时字库需要缓存到系统。参考《浅析Android字体加载原理》博文。缓存字库的代码如下,代码以android7.1.1为例:
frameworks\base\graphics\java\android\graphics\Typeface.java
private static void init() {
// Load font config and initialize Minikin state
File systemFontConfigLocation = getSystemFontConfigLocation();
File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
try {
FileInputStream fontsIn = new FileInputStream(configFilename);
FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
List<FontFamily> familyList = new ArrayList<FontFamily>();
// Note that the default typeface is always present in the fallback list;
// this is an enhancement from pre-Minikin behavior.
for (int i = 0; i < fontConfig.families.size(); i++) {
FontListParser.Family f = fontConfig.families.get(i);
if (i == 0 || f.name == null) {
familyList.add(makeFamilyFromParsed(f, bufferForPath));
}
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
setDefault(Typeface.createFromFamilies(sFallbackFonts));
Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
for (int i = 0; i < fontConfig.families.size(); i++) {
Typeface typeface;
FontListParser.Family f = fontConfig.families.get(i);
if (f.name != null) {
if (i == 0) {
// The first entry is the default typeface; no sense in
// duplicating the corresponding FontFamily.
typeface = sDefaultTypeface;
} else {
FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
FontFamily[] families = { fontFamily };
// 除了第一个字库,其他字库都会有自己的缓存对象
typeface = Typeface.createFromFamiliesWithDefault(families);
}
systemFonts.put(f.name, typeface);
}
}
for (FontListParser.Alias alias : fontConfig.aliases) {
Typeface base = systemFonts.get(alias.toName);
Typeface newFace = base;
int weight = alias.weight;
if (weight != 400) {
newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
}
systemFonts.put(alias.name, newFace);
}
sSystemFontMap = systemFonts;
} catch (RuntimeException e) {
Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening " + configFilename, e);
} catch (IOException e) {
Log.e(TAG, "Error reading " + configFilename, e);
} catch (XmlPullParserException e) {
Log.e(TAG, "XML parse exception for " + configFilename, e);
}
}
static {
// 解析fonts.xml
init();
// Set up defaults and typefaces exposed in public API
// 缓存不同的字库,以下部分为基础字库
DEFAULT = create((String) null, 0);
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
SANS_SERIF = create("sans-serif", 0);
SERIF = create("serif", 0);
MONOSPACE = create("monospace", 0);
sDefaults = new Typeface[] {
DEFAULT,
DEFAULT_BOLD,
create((String) null, Typeface.ITALIC),
create((String) null, Typeface.BOLD_ITALIC),
};
}
private static File getSystemFontConfigLocation() {
// 字库配置文件fonts.xml放置位置
return new File("/system/etc/");
}
删减不必要的缓存字库
删除字库文件
在Android7.1.1上,完整的字库缓存,至少需要57M;实测删减不需要的字库后,可以省出20M内存(主要是中文字库太大,17M,其他基本是KB)。
明白了字库功能和字库使用方式,删减缓存字库简单多了吧
删减字库策略
- 明确字库功能
- 在字库配置文件中删掉不需要字库的配置
- 运行软件,通过log查看,那些字库加载不到,然后修改fonts.xml。log如下
02-19 19:18:54.185 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansNKo-Regular.ttf
02-19 19:18:54.186 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansOlChiki-Regular.ttf
02-19 19:18:54.187 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansRejang-Regular.ttf
02-19 19:18:54.187 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansSaurashtra-Regular.ttf
02-19 19:18:54.188 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansSundanese-Regular.ttf
02-19 19:18:54.188 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansSylotiNagri-Regular.ttf
02-19 19:18:54.189 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
02-19 19:18:54.190 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTagbanwa-Regular.ttf
02-19 19:18:54.190 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTaiTham-Regular.ttf
02-19 19:18:54.191 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTaiViet-Regular.ttf
02-19 19:18:54.191 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTibetan-Regular.ttf
02-19 19:18:54.192 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTibetan-Bold.ttf
02-19 19:18:54.192 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansTifinagh-Regular.ttf
02-19 19:18:54.193 1640 1640 E Typeface: Error mapping font file /system/fonts/NotoSansVai-Regular.ttf
02-19 19:18:54.220 1640 1640 E Typeface: Error mapping font file /system/fonts/DroidSansFallback.ttf
如果仅仅是节省内存,到这里就结束了。如果还需要把不需要的字库从ROM中删掉,需要修改编译文件
- 确定要删除的字库文件名
- 从编译配置文件中删除对应的字库名
另外一种删除不需要的文件,需要根据Android编译规则,自行定义一个规则。我这里有一个,不在这里添加了,在另一篇文章说明(目前还未写,编译配置的核武器啊!)。
配置系统支持的字库
删除字库,需要配合配置系统支持的字库,否则开机系统初始化时,依然会去加载已删除的字库,会报一些错误(无影响),虽然无影响,但是秉承一个优良程序的实现,应该消除这些错误。
配置系统支持的字体,在设备目录下(如device/google)添加编译说明:
# 支持简体中文及英文,第一语言时系统默认语言
PRODUCT_LOCALES := zh-rCN en-rGB