Android Emoji 最佳實踐

hamberluo發表於2018-08-21

Introduction

由於 android 系統的碎片化,導致了最新的 emoji 在老的手機上不支援。
微信也做得不好,很多 emoji 表情直接變成了 X ,不知道什麼意思。
解鈴還須繫鈴人, Android 官方為此做了不少努力。

Android Official Solution

EmojiCompat 解決方案

EmojiCompat 的方案包含如下兩種方式

  • Downloadable fonts configuration (網路下載)
  • Bundled fonts configuration (本地打包)把一個 7.4M 的 ttf 檔案塞進 apk 中

這兩種方式都有缺陷:

  • 網路下載方式依賴於翻牆,門檻比較高
  • 本地打包方式對 apk 的體積增大不可接受

有沒有一種方式既不用翻牆又不增大 apk 的體積呢?

答案是有的

Best Practice

無論是網路下載還是本地打包,都涉及到讀取 font 檔案進行渲染,那麼這裡有兩步

  • 接管讀取的流程
  • 接管渲染的流程

Hook Loading Process

首先,我們在 gradle 中匯入如下依賴

compile "com.android.support:support-emoji:$version"
compile "com.android.support:support-emoji-appcompat:$version"
compile "com.android.support:support-emoji-bundled:$version"
複製程式碼

這三個依賴所包含的東西不同

  • support-emoji 包含 EmojiCompat 方案所需要的基本實現以及 Downloadable fonts configuration 的程式碼
  • support-emoji-appcompat 主要是針對 AppCompat 的 UI 元件的支援
  • support-emoji-bundled 是 Bundled fonts configuration 的程式碼

然後根據官網解決方案,編譯打包成 apk 後,解壓 apk ,取出 NotoColorEmojiCompat.ttf 檔案(建議使用最新版本的 support 包) 放著備用,一會兒下鍋(寬油警告?),哦不,上傳到七牛或者其他雲端儲存

接下來就不需要 support-emoji-bundled 了,也不需要 support-emoji ,因為 support-emoji-appcompat 包含了

新建一個類

class DownloadEmojiCompatConfig : EmojiCompat.Config(DownloadMetadataLoader()) {
    private class DownloadMetadataLoader internal constructor() : EmojiCompat.MetadataRepoLoader {
        @RequiresApi(19)
        override fun load(loaderCallback: EmojiCompat.MetadataRepoLoaderCallback) {
            // 這裡只是關鍵部分,並不表示只有這一行
            loaderCallback.onLoaded(MetadataRepo.create(Typeface.createFromFile(file.absolutePath), FileInputStream(file)))
        }
    }
}
複製程式碼

其中 file 就是我們下載好的 ttf 檔案

注意事項:

  • 整個 EmojiCompat 只適用於 4.4 以上,再往下的就不支援了(也夠用了)
  • 既然是想通過下載來解決本地安裝包增大的問題,那麼就好考慮好 wifi 4g 等情況了, 7M 流量呢

然後就是 EmojiCompat 自己的初始化流程了

EmojiCompat.init(
        DownloadEmojiCompatConfig()
                .setReplaceAll(false)
                .registerInitCallback(object : EmojiCompat.InitCallback() {
                    override fun onInitialized() {
                        emojiReady = true
                    }

                    override fun onFailed(throwable: Throwable?) {
                        emojiReady = false
                    }
                })
複製程式碼

注意事項:

  • 放到其他執行緒來做,這個初始化時間至少 150ms
  • 利用 InitCallback 回撥來處理 ready 狀態, ready 了後面渲染才可以用
  • 小於 4.4 就不要初始化了
  • setReplaceAll 方法這裡重點說明下
    • true 表示所有的 emoji 都替換為 ttf 裡的,這樣可以保證 emoji 風格統一
    • false 表示優先使用系統支援的 emoji ,不支援才用 ttf 裡的

此處使用 setReplaceAll(false) 是因為 ttf 裡的 emoji 在渲染國旗的時候會出現問題(也包括中國國旗)

到這裡,完成了 EmojiCompat 的 loading 流程

Hook Rendering Process

Activity 的 LayoutInflaterCompat.setFactory2 替換 xml 裡的 View

新寫 EmojiTextView 繼承 AppCompatTextView

@Override
public void setText(CharSequence text, BufferType type) {
    if (emojiReady) {
        super.setText(EmojiCompat.get().process(text), type);
        return;
    }
    super.setText(text, type);
}
複製程式碼

EditText 就直接替換為 support-emoji-appcompat 裡的 EmojiAppCompatEditText 即可

到這裡,完成了 EmojiCompat 的 rendering 流程


事情到這裡就結束了麼?並沒有,因為新的 emoji 一直會出來,而 android 的 support 包並不會及時更新

那怎麼辦,有一個開源的組織叫 emojione 這裡 是它的官網

這個組織會發布自己的一套 emoji ttf 來相容各種裝置和統一不同風格的 emoji

也別高興得太早,上一次更新是 v3 版本,落後於 Android 提供的 support 包的版本(就是支援的 emoji 比官方少)

不過,最近,它出了 v4 了,可以期待下,但目前 ttf 版本還是 coming soon 狀態,可以訂閱下以便及時收到郵件提醒

Job Ad

歡迎正在刷 即刻app 的你加入我們,一起參與千萬級使用者產品的研發和運營!

招聘崗位列表

當然也包括 Android 職位啦

工作地點:上海市楊浦區創智天地
簡歷請傳送至 hr@okjike.com 並在郵件標題中註明職位 + 姓名

一起打造明天的即刻 (^U^)ノ~YO

相關文章