Android字体系统由android 2D图形引擎skia实现,字体系统的配置方法在各个版本中不完全相同,按照API level可以划分为三个阶段:4.0以下版本、4.0-4.4版本、5.0及以上版本。本文主要针对4.0及以上版本中字体系统的配置方法及字体相关应用进行分析。注意,浏览器及webView中的字体有单独的字体系统。
Android中将每个字体文件描述为字型(Typeface),包括字体族(FontFamily)和字体样式(textStyle)两个维度,字体族对应各种字体类型,样式则分为normal、bold、italic和bold-italic四种。
工作原理
下面对字体工作原理的分析围绕系统字体配置文件解析与字体加载相关内容,不涉及skia的实现细节,为便于理解分为三个部分。
1. java层
在这一层,android.graphics.Typeface类负责加载系统字体,并对上层提供创建字体功能调用。下面分析Typeface类的调用过程。
在android启动过程中,ZygoteInit类的入口函数main()中调用preload()函数,preload()函数主要用于加载并初始化各种类、链接库、资源等。
static void preload() {
Log.d(TAG, "begin preload");
preloadClasses();
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
Log.d(TAG, "end preload");
}
在preload()中调用了preloadClasses()函数用于加载并初始化一些系统常用类,这些类的列表位于frameworks/base/preloaded-classes文件中,其中包括了Typeface类。
/**
* Performs Zygote process initialization. Loads and initializes
* commonly used classes.
*
* Most classes only cause a few hundred bytes to be allocated, but
* a few will allocate a dozen Kbytes (in one case, 500+K).
*/
private static void preloadClasses() {
......
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(PRELOADED_CLASSES);
......
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line);
......
}
preloadClasses()中调用Class.forName("android.graphics.Typeface")加载Typeface类,并调用Typeface类的static块。create()通过调用相应的native方法创建字体对象。
// 4.x
static {
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 native int nativeCreate(String familyName, int style);
private static native int nativeCreateFromTypeface(int native_instance, int style);
private static native void nativeUnref(int native_instance);
private static native int nativeGetStyle(int native_instance);
private static native int nativeCreateFromAsset(AssetManager mgr, String path);
private static native int nativeCreateFromFile(String path);
// 5.x
static {
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 native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateWeightAlias(long native_instance, int weight);
private static native void nativeUnref(long native_instance);
private static native int nativeGetStyle(long native_instance);
private static native long nativeCreateFromArray(long[] familyArray);
private static native void nativeSetDefault(long native_instance);
注意4.x版本与5.x版本提供的上层API调用是一致的,但是c++层的native函数却并不一致,这与5.x版本中static块增加了一个init()函数调用有关,init()方法主要实现了解析系统字体配置文件,并据此加载系统字体。对于4.x版本,解析并加载系统字体的功能在c++层的skia代码中实现,留到下面进行分析。
private static void init() {
// Load font config and initialize Minikin state
File systemFontConfigLocation = getSystemFontConfigLocation();
File configFilename = new File(systemFontConfigLocation, F