2018 年 5 月,SuperSU 的作者 Chainfire 正式宣布停止開發所有 root 相關的應用,這個消息曾一度讓許多人認為是 Android 玩機時代的落幕。
用塵封之淚大神的話來說,就是:
Chainfire 是真正的大神,能夠做到像 Chainfire 這樣的人並不多。
但誰也沒想到,此前一直不溫不火的 Magisk 卻在這個時候站了出來並成功扛過了 root 工具的重擔。現在,Magisk 不僅做到了 Android Q 一問世就立即解決了 root 問題,還為玩機的朋友提供了掛載各種優化模塊,甚至結合一些開發者的作品在 Androd 9 上實現了需要依賴 Xposed 框架才能實現的功能(在 Xposed 的作者 rovo89 自己都還沒搞定 Android 9 適配的前提下,這是一件非常 amazing 的事情)。
不過,今天主要講通過 Magisk 換字體的話題,Magisk 模塊字體為什麼這麼好用?
Android 是如何呈現字體的?
要回答上面這個問題,得先從 Android 手機的字體調用規則說起。
Android 5.0 之後,幾乎整個手機的字體效果都由 fonts.xml 這個配置文件來掌控,它位於 system/etc 路徑下,起到對 system/fonts 路徑下的字體文件進行全局調配的中樞級作用。
之所以用上「幾乎」這個詞,是因為 Android 手機環境複雜,各大廠商喜歡魔改系統,也難免動到和字體有關的地方。因此通過修改 fonts.xml 這個配置文件以及 system/fonts 這些字體文件也許只能實現 95% 左右的字體替換覆蓋效果,只有在原生安卓系統上才能 100% 全局生效。
為什麼要進行這樣的修改呢?在 fonts.xml 中,有一段很關鍵的代碼:
<family name="sans-serif">
<font weight="100" stylex="normal">Roboto-Thin.ttf</font>
<font weight="100" stylex="italic">Roboto-ThinItalic.ttf</font>
<font weight="300" stylex="normal">Roboto-Light.ttf</font>
<font weight="300" stylex="italic">Roboto-LightItalic.ttf</font>
<font weight="400" stylex="normal">Roboto-Regular.ttf</font>
<font weight="400" stylex="italic">Roboto-Italic.ttf</font>
<font weight="500" stylex="normal">Roboto-Medium.ttf</font>
<font weight="500" stylex="italic">Roboto-MediumItalic.ttf</font>
<font weight="900" stylex="normal">Roboto-Black.ttf</font>
<font weight="900" stylex="italic">Roboto-BlackItalic.ttf</font>
<font weight="700" stylex="normal">Roboto-Bold.ttf</font>
<font weight="700" stylex="italic">Roboto-BoldItalic.ttf</font>
</family>
即使沒有什麼程序員基礎也不難看懂這段代碼的意思,沒錯,font.xml 控制了 Android 操作系統在不同 UI 界面中的字體粗細,告訴系統該在哪些地方分別調用哪一款 Roboto 字體。
Roboto 是 Google 為 Android 操作系統設計的一個無襯線字體家族,原本包含 Thin、Light、Regular 、Medium、Bold、Black 共計 6 套字重(即 weight),同時每個字重還有斜體版本(即 italic)。
此外,Roboto 字體家族的部分字重還有窄版的 Condensed 及對應的斜體,它們在 Android 操作系統中通常用作桌面啟動器應用抽屜的圖標標籤,保證在有限的空間內顯示更完整的應用名稱。這種設定同樣在 fonts.xml 這個文件里有所體現。
不難看出,fonts.xml 就像是一本字典,Android 操作系統在需要調用某一個字體效果時就會在這當中進行查詢,然後按圖索驥,找到並調用 system/fonts 下對應的字體文件,最後我們就在界面上看到了字體效果,是不是很簡單?
你每天都在看的中文字體並不完美
我們繼續用這個思路繼續往下看 Android 操作系統中中文字體的調用機制。
在 Android 5.0 和 6.0 版本中,中文字體的控制代碼如下:
<family lang="zh-Hans">
<font weight="400" stylex="normal">NotoSansSC-Regular.otf</font>
</family>
在 Android 7.0 到 8.1 版本中,中文字體的控制代碼變成了這樣:
<family lang="zh-Hans">
<font weight="400" stylex="normal" index="2">NotoSansCJK-Regular.ttc</font>
</family>
而在 Android 9 之後,中文字體的控制代碼改成了這樣:
<family lang="zh-Hans">
<font weight="400" stylex="normal" index="2">NotoSansCJK-Regular.ttc</font>
<font weight="400" stylex="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
這三套代碼基本大同小異,早期版本調用思源黑體的 OpenType 文件,後來改成了思源黑體的 TrueType Collection 文件,並且用額外的 index 代碼指定了集成字型檔中的文件序號,再後來又追加了缺字回退機制(即思源黑體里沒有的符號用思源宋體來顯示)。
不過仔細觀察和對比後卻不難發現,Google 在中文字體調用機制上偷了懶:和英文、數字字體中使用 6 套不同粗細字體並且還有斜體、窄版等做法不同,中文環境下 Google 僅用了 font weight=400 來敷衍了事。
這種區別對待給我們帶來了什麼問題呢?
我們以谷歌原生系統默認的「思源黑體單字重」效果和我優化後的「思源黑體多字重」效果作比較: