在移動互聯網如此發達的今天,Emoji 已無處不在,并成為我們日常交流中不可或缺的一部分。據統計,Emoji 的使用率在過去 10 年內不斷攀升,2021 年更是達到了歷史新高,每天有超過五分之一的推文中包含了 Emoji,一些應用上的用戶每天發送的 Emoji 數量更是達到了數十億。然而用戶在 Android 平臺上使用 Emoji 時卻存在著一些問題,本文將針對這些問題進行探討,并向您介紹 Emoji 的工作原理以及 Android 平臺近期關于 Emoji 的更新。
Emoji 在 Android 平臺的現狀
Emoji 在 Android 平臺存在的問題
Unicode 每年都會對 Emoji 標準進行更新,用戶越來越頻繁地使用各種 Emoji,但卻存在有約 96% 的 Android 用戶無法正確查看新發布 Emoji 的問題,而 iOS 平臺只有 16%,這一比例明顯高出許多。
另外根據統計排名,前 100 位的 Emoji 占據了所有用戶日常使用 Emoji 總量的 82%,但在約 20% 的情況下,當用戶發送一個 Emoji 后,對方看到的卻是豆腐塊或一張損壞的圖像,這種情況直接導致用戶無法正確通過 Emoji 來傳遞自己的本意。如下圖所示,用戶發送了一張含淚的笑臉,但對方卻只收到一個中間有 X 的方塊 (我們稱其為豆腐塊)。
△Emoji 在發送和接受方的不同顯示效果
隨著用戶的增多,Unicode 也在不斷增加新的 Emoji 來體現多元化和包容性,但是 Android 卻并不能完全兼容這些新版的 Emoji,不同的 Android 版本對 Emoji 的支持程度也不同。比如以下的幾個例子:
△Emoji 無法在不同 Android 版本間正確表達多樣性和包容性
- 在 Android 7.0 Nougat 和更早版本上無法正確通過 Emoji 表示膚色。用戶發送了一個表示深膚色手臂的 Emoji,但對方收到的卻是一個手臂和深色方塊的分解版本。
- 在 Android 8.0 Oreo 和更早版本上無法正確顯示代表中性的 Emoji。
- 在 Android 9 Pie 和更早版本上不支持顯示多人多膚色的 Emoji。
Android 平臺針對 Emoji 的解決方案
以上問題顯然會導致非常糟糕的用戶體驗,并且不利于用戶之間通過 Emoji 進行交流。我們的目標就是確保所有 Android 用戶無論是使用哪種應用,都能夠正常地使用每個 Emoji。為此,從 Android 12 開始,我們引入了可更新系統字體 (Updatable System Fonts),首先引入的便是 Emoji 字體,這也意味著開發者再也不需要在搭載 Android 12 以及 12 以上版本的設備中考慮 Emoji 適配的問題了,系統將默認支持新的 Emoji。
但是考慮到時光機還沒有被發明出來,我們也沒辦法穿越到過去把可更新系統字體塞到舊版本的設備中去。如果一直等待時光機被發明出來而什么也不做的話,Android 12 版本之前的設備就會一直顯示豆腐塊,或者以其他錯誤的方式進行渲染。因此我們還是做了些改進,讓您可以通過更新 Jetpack 庫
EmojiCompat
解決這一問題:
https://developer.android.google.cn/guide/topics/ui/look-and-feel/emoji-compat
EmojiCompat 早在幾年前就已經發布,2021 年我們對其做了很多改進,并將其整合至AppCompat 1.4版本中。為此我們開發了一個新庫 androidx.emoji2,并添加了自動配置選項,它可自行配置以加載正確的字體。我們將這個庫集成到了 AppCompat 1.4 中,也就是說,您僅需升級至 AppCompat 1.4 版本,便可在 API 19 及更高的版本上正常顯示新加入的 Emoji,開箱即用,無需任何額外配置。
-
AppCompat 1.4 版本
https://developer.android.google.cn/jetpack/androidx/releases/appcompat#1.4.1 -
androidx.emoji2
https://developer.android.google.cn/jetpack/androidx/releases/emoji2
AppCompat 1.4 針對 Emoji 的優化
△Emoji2 和 Emoji 對比
Emoji2 庫是 AppCompat 庫的一個新的依賴項,雖然它會代替現有的 androidx.emoji 庫,但是 API 幾乎相同。在此次更新中,我們使用 androidx.startup 添加了新的初始化程序 (EmojiCompatInitializer),添加了新的默認配置,并且全部支持了 nullability 注解。另外,相較于 androidx.emoji,我們還刪除了一些在使用 AppCompat 時不再需要的 TextView 子類,這使得在 RA 之后節省了約 14KB 的大小。
- Emoji2https://developer.android.google.cn/jetpack/androidx/releases/emoji2
- androidx.emojihttps://developer.android.google.cn/jetpack/androidx/releases/emoji
△Emoji2 加載 Emoji 的步驟
在這次更改中,一大新特性便是 EmojiCompatInitializer,它是一個使用了 androidx.startup 庫的初始化程序,在應用啟動時會自動配置 EmojiCompat。我們已對該初始化程序的性能進行了大量的調整,對于大多數應用來說使用默認配置已經完全足夠,但如果您需要對應用啟動做納秒級別的優化,則可以考慮移除 startup 庫和創建線程所帶來的消耗。首先,確保先初始化 EmojiCompat,再執行 Activity.onCreate,這可以保證每個 TextView 都能顯示新版 Emoji。然后,可以像 EmojiCompatInitializer 一樣將 Emoji 字體加載延遲到首屏繪制之后,這樣做是因為雖然加載過程是在后臺線程中進行的,但它還是執行大量的網絡和磁盤 I/O 操作,這些操作會同首屏加載一起搶奪資源。
這里再次強調,我們已對該初始化程序的性能進行了大量的調整和優化,除非必要,請使用 EmojiCompatInitializer 的默認實現。
另一個重要的功能是默認配置,在 androidx.emoji 中您需要從文檔的示例代碼中復制一些模版配置 (類似于以上代碼) 到應用中,而 Emoji2 中我們添加了可以直接用于 EmojiCompatInitializer 的 DefaultEmojiCompatConfig,如下代碼所示,只需一行簡單配置即可,當然也支持手動配置的需求。
在 AppCompat 中,我們將 Emoji2 集成到了所有的視圖中,這意味著所有視圖都可以支持新版 Emoji,如果您的 Activity 繼承了 AppCompatActivity,在 XML 中直接使用 TextView 或 EditView 即可。AppCompatActivity 安裝了一個布局填充器 (LayoutInflater),它會用 AppCompatTextView 來替換 TextView 等視圖,在代碼中無論何時創建 TextView,都應該確保創建的是 AppCompatTextView,并且自定義視圖應該繼承相應的 AppCompat 子類。
所有集成了 Emoji2 的視圖都有一個 EmojiCompatEnabled 屬性,通過它可以控制是否開啟 EmojiCompat,該屬性還提供了 getter 和 setter 方法。EmojiCompatEnabled 屬性有助于在知道文本絕不可能包含 Emoji 的情況下,來規避執行 Emoji 的處理邏輯,雖然即使不規避該邏輯所帶來的成本也是極低的,但在某些情況下每一納秒都至關重要,此屬性便是為了支持這種情況。另外,該屬性對于在后臺線程上處理 Emoji 也很有幫助,AppCompat 對于 Emoji2 的集成會在 setText 之后的適當時間調用 EmojiCompat.process,您可以通過 EmojiCompatEnabled 屬性禁用此方法調用,并對 Emoji 的處理移至后臺線程。但通常這種優化沒必要,除非是在 RecyclerView 中展示大量的文本導致卡頓,那么可以考慮采用這一優化方案。
測試新版 Emoji
由于集成了可下載字體,對于測試新版的 Emoji 并不是那么容易。要創建一個不會導致誤報或漏報的通用自動化測試庫很難,而大多數開發者在實際情況下會直接手動測試 EmojiCompat 的集成,因此最好的選擇還是使用一個記錄了用于測試的 Emoji 列表,同樣此方式對于手動測試或對屏幕截圖進行測試也都非常有用。
如果您希望了解更多信息,請查看文檔:支持新式表情符號。我們在文檔中為您提供了一些關于配置測試模擬器和設備所需要的一些操作建議:
https://developer.android.google.cn/guide/topics/ui/look-and-feel/emoji2
Emoji 渲染原理
△一組碼點
Emoji 屬于一種圖形字符,是字符串的一部分。它就像字母 "I"一樣,只是繪制方式和從屬的字體文件不同而已。但是對于計算機來說,它并不會特意關心什么是 Emoji 或字母 "I",一個字符串本質上就只是一組碼點,其中的數字通過 Unicode 進行分配,代表著計算機上會出現的每一個字符。
現在 Unicode 并不僅僅只是一種格式了,它還代表了制定該標準的委員會,委員會會決定一些事情,比如數字 7 代表字母 "I" (實際上 7 并非真正代表字母 I 的碼點,此處僅僅是舉個例子)。那么當您試圖在 Android 上渲染上述表示字符串的碼點時會發生什么呢?
首先,Android 會根據碼點和應用要求使用的字體樣式為每個字符找出最佳字體。當前 Android 上非斜體且正常粗細 "V"的默認字體是 roboto-regular.ttf,Android 會對字符串進行遍歷,檢查每個字符并查找最佳字體。它會檢查碼點和樣式,您可以對字符串進行樣式的定制操作,比如對一些字符進行加粗等等。對于上述簡單的字符串來說,它就只是會選擇 roboto-regular.ttf 字體。
△遍歷碼點查找正確的字符串
但是,當遇到 Emoji 字符時,您可能會覺得它會進行完全不同的渲染方式,畢竟它看起來不像任何其他的字母。但實際上,Emoji 就是個文本,由碼點表示,同字母 "I"和 "I"一樣沒什么區別,繪制它的方式就存儲到了字體中。Android 會首先嘗試在字體中查找無斜體且正常粗細的 "融化臉",但這一次發現在 roboto-regular 中并沒有想要的結果,便會去 NotoColorEmoji 中進行查找,這是 AOSP 上預裝的 Emoji 字體,它包含了每個 Emoji 的圖像,在 Android 平臺上通過這種字體繪制 Emoji 和繪制字母 "I"的方式完全相同,都是查找字體文件后在屏幕上繪制出來。
△通過 NotoColorEmoji 對 Emoji 字符進行繪制
在 Android 12 及以上版本中,平臺可以確保 Emoji 會正常顯示,因為可更新系統字體會將新版 Emoji 添加到字體文件中。但對于 Android 12 之前的版本,我們沒有任何方法可以更新字體,這意味著 Android 不知道用什么字體來繪制 "融化臉",此時它會改為繪制一個稱為豆腐塊的備用字形。這里就是 Emoji2 開始大展身手的地方了。
△Emoji2 對 Emoji 字符的繪制過程
在將字符串發送到 Android 系統之前,在字符串上會調用 EmojiCompat.process 方法,此調用將遍歷并查找那些系統不知道如何繪制的 Emoji,并為每個 Emoji 添加一個 EmojiSpan,這是一個替換 Span,這意味著它將只替換該段字符串中對應的內容。系統會直接使用 roboto-regular.ttf 正常繪制,但當找到 EmojiSpan 時它會將繪制權轉交給 Span。
在該 Span 中 Android 使用了兩個方法,首先,它會獲取字符尺寸并告訴 Android 要在文本布局中為此 Span 保留多少空間,然后,當需要繪制字符串時,它將調用 EmojiSpan 上的 draw 而非自行繪制。在 EmojiSpan 中,它知道 Compat 版的 Emoji 字體位置,并能直接從中繪制出 "融化臉"。再返回到渲染階段,平臺將調用 EmojiSpan.draw,整個區域將由 EmojiSpan 進行繪制,而非平臺。實際上,從平臺的角度來看 EmojiSpan 只是在字符串中間繪制了一張圖片,并沒有別的特殊操作。
總結
本文通過分析 Emoji 在 Android 平臺存在的問題,針對不同版本的 Android 系統介紹了兩種解決方案:
- Android 12 及以上的版本使用可更新系統字體,無需開發者手動適配;
-
Android 12 以下的版本集成 AppCompat 1.4 也可自動適配新版 Emoji,無需額外操作。
此外,我們還介紹了 Emoji 的渲染原理,讓您更進一步了解 Emoji 是如何呈現在屏幕上的。請記得升級 AppCompat 到 1.4 版本,為用戶提供支持新版 Emoji 的最佳體驗。
來源:谷歌開發者
-
Andorid
+關注
關注
0文章
7瀏覽量
6997
發布評論請先 登錄
相關推薦
評論