天天看點

說說 bytex 的 extension (1) access-inline

本文談論一個比較小的内容,說一下 byteX 自帶的一個插件 —— ”access-inline“ 的實作 。

衆所周知,Android 應用的方法數量是有過一些問題的,因為一個 dex 裡的方法數量不能超過 65535 ,是以當項目達到一定程度後,就不能夠使用一個 dex 檔案來承載所有的位元組碼了,這時候我們就需要引入 multidex 的處理了,将一個 dex 拆分為多個 dex 甚至是許多個 dex 。

這個問題解決了,也就可以不需要擔心方法數量過多導緻無法打包了。

但是方法數量太多了,也會導緻一些不是問題的小問題,例如

包的大小增加

混淆 mapping 檔案裡的項目增加

運作時的執行的位元組碼指令增加

這些問題,其實對于一般的産品開發而言,都是無傷大雅的問題,不處理也是沒有問題的。而本文談論的這個 byteX 的插件,就是用來優化這個”方法數量過多“的問題滴。

和 Android 布局檔案裡使用 減少嵌套層級,以及 kotlin 裡使用 inline 修士方法減少方法調用層級類似,access-inline 這個方法也是将一部分方法調用改為直接調用,借此來實作”減少方法數量“的目的。

java 語言裡有一種類叫做 ”内部類“,内部類可以直接通路其外部類的屬性和方法。實際在位元組碼的實作裡,内部類也是一個獨立的類,隻不過持有了外部類的引用而已。

而其之是以可以通路外部類的屬性和方法,是因為 java 在編譯的時候,專門為這個内部類生成了若幹個靜态方法,将内部類對外部類的通路,都轉成了對這個靜态方法的調用了。而本文要說的 access-inline 插件,就是要将這部分方法改為代碼内聯,也就是改為直接調用,而不是通過調用生成的靜态方法擷取屬性或者是執行方法。

我們這樣想:java 編譯器處理内部類的時候,做了如下兩件事情:

為内部類通路外部類的屬性/方法 生成靜态方法用做代理。

将内部類對外部類的屬性/方法 的擷取調用改為對上面提到的方法的調用。

而 access-inline 插件的目标是 : 内部類通路外部類的 屬性/方法 的時候直接進行 擷取/調用。

那麼對于上述兩點分别解決即可,改動點就是:

修改後,内部類對外部類的 屬性/方法 的擷取/調用 就是直接通路的了,是以原來額外生成的方法已經不需要了,将其删除掉。

将位元組碼生成的通過靜态方法通路的方式,改為直接進行通路。

這裡面涉及到一些細節:

删除方法,那麼哪些方法需要删除?

将通路靜态方法修改為直接進行通路

哪些靜态方法需要修改?這個問題和問題 1. 是一樣的,等下做簡略的回答。

怎麼修改位元組碼?

上述的問題都是可以解決的,本文盡量避過源碼級别的解釋,使用白話文講解核心的原理,因為需要了解源碼的人,是可以去 github 上找源碼進行檢視的,本文就不解釋源碼了。

問題 1, 哪些方法需要删除 ? 因為這類方法是 java 編譯器生成的,是以我們需要盡量確定判斷條件隻命中這類生成的方法,而不會将項目裡正常的方法”誤殺“。這涉及了 java 編譯器的處理邏輯,在此我直接給出 access-inline 官方的判斷條件:

是生成的方法 (位元組碼中的方法,有一個标記位 <code>isSynthetic</code> 是用來表示是否是生成的方法的。)

是靜态方法。

方法名稱以 “access$” 開頭。這點是 java 編譯器的規定。

問題 2,怎麼修改位元組碼?這個需要位元組碼處理功能,在 byteX 中,是使用 asm 來進行處理的,借助 asm 的架構可以實作位元組碼的增加、删除、修改等操作。

byteX 中自帶的插件,都是比較有趣的功能。推薦有時間精力并且感興趣的朋友可以去 github 上檢視,這種有趣的插件,隻是學習其功能,也可以學習到很多知識,擴大眼界。例如 asm 處理位元組碼等。

byteX 官方位址 :

[https://github.com/bytedance/ByteX](