主頁 > 移動端開發 > 插件化工程R檔案瘦身技術方案 | 京東云技術團隊

插件化工程R檔案瘦身技術方案 | 京東云技術團隊

2023-06-14 10:16:29 移動端開發

隨著業務的發展及版本迭代,客戶端工程中不斷增加新的業務邏輯、引入新的資源,隨之而來的問題就是安裝包體積變大,前期各個業務模塊通過無用資源刪減、大圖壓碩訓轉上云、AB實驗業務邏輯下線或其他手段在降低包體積上取得了一定的成果,

在瘦身的程序中我們關注到了R檔案瘦身的概念,目前京東APP是支持插件化的,有業務插件工程、宿主工程,對業務插件包檔案進行分析,發現除了常規的資源及代碼外,R類檔案大概占包體積的3%~5%左右,對宿主工程包檔案進行分析,R類檔案占比也有3%左右,我們先后在對R類檔案瘦身的可行性及業界開源專案進行調研后,探索出了一套適用于插件化工程的R檔案瘦身技術方案,

理論基礎—R檔案

R檔案也就是我們日常作業中經常打交道的R.java檔案,在Android開發規范中我們需要將應用中用到的資源分別放入專門命名的資源目錄中,外部化應用資源以便對其進行單獨維護,

外部化應用資源后,我們可在專案中使用R類ID來訪問這些資源,且R類ID具有唯一性,

public class MainActivity  extends BaseActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

在android apk打包流程中R類檔案是由aapt(Android Asset Packaing Tool)工具打包生成的,在生成R類檔案的同時對資源檔案進行編譯,生成resource.arsc檔案,resource.arsc檔案相當于一個檔案索引表,應用層代碼通過R類ID 可以訪問到對應的資源,

R檔案瘦身的可行性分析

日常開發階段,在主工程中通過R.xx.xx的方式參考資源,經過編譯后R類參考對應的常量會被編譯進class中,

 setContentView(2131427356);

這種變化叫做行內,行內是java的一種機制(如果一個常量被標記為static final,在java編譯的程序中會將常量行內到代碼中,減少一次變數的記憶體尋址),

非主工程中,R類資源ID以參考的方式編譯進class中,不會產生行內,

 setContentView(R.layout.activity_main);

產生這種現象的原因是AGP打包工具導致的,具體細節,大家可以去查閱一下android gradle plugin在R檔案上的處理程序,

結論:R類id行內后程式可運行,但并非所有的工程都會自動產生行內現象,我們需要通過技術手段在合適的時機將R類id行內到程式中,行內完成后,由于不再依賴R類檔案,則可以將R類檔案洗掉,在應用正常運行的同時,達到包瘦身目的,

插件化工程R檔案瘦身實戰

制定技術方案

目前京東Android客戶端是支持插件化的,整個插件化工程包含公共庫(是一個aar工程,用來存放組件和宿主共用的類和資源)、業務插件(插件工程是一個獨立的工程,編譯產物可以運行在宿主環境中)、宿主(主工程,提供運行環境),在插件化的程序中為了防止宿主和插件資源沖突,通過修改插件packageId保證了資源的唯一性,由于公共資源庫、宿主是被很多業務依賴,對這兩個專案進行改動評估影響涉及比較多,插件一般都是業務模塊自行維護,不存在被依賴問題,所以先在業務插件模塊進行R類瘦身實踐,

對業務插件工程打出的包進行反編譯以后,發現R類ID無行內現象,且R類檔案具有一定的大小,對包內的R檔案進行分析,發現R檔案中僅包含業務自身的資源,不包含業務依賴的公共資源R類,

public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle) {
    this.b = paramLayoutInflater.inflate(R.layout.lib_pd_main_page, paramViewGroup, false);
    this.h = (PDBuyStatusView)this.b.findViewById(R.id.pd_buy_status_view);
    this.f = (PageRecyclerView)this.b.findViewById(R.id.lib_pd_recycle_view);}

結合對業界開源專案的調研分析,嘗試制定符合京東商城的技術方案并優先在業務插件內完成R類ID行內并洗掉對應的R檔案,

1.通過transformapi 收集要處理的class檔案

Transform 是 Android Gradle 提供的操作位元組碼的一種方式,它在 class 編譯成 dex 之前通過一系列 Transform 處理來實作修改.class檔案,

@Override
public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
super.transform(transformInvocation);
//  通過TransformInvocation.getInputs()獲取輸入檔案,有兩種
//  DirectoryInpu以原始碼方式參與編譯的目錄結構及目錄下的檔案
//  JarInput以jar包方式參與編譯的所有jar包
    allDirs = new ArrayList<>(invocation.getInputs().size());
    allJars = new ArrayList<>(invocation.getInputs().size());
    Collection<TransformInput> inputs = invocation.getInputs();
    for (TransformInput input : inputs) {
        Collection<DirectoryInput> directoryInputs = input.getDirectoryInputs();
         for (DirectoryInput directoryInput : directoryInputs) {
               allDirs.add(directoryInput.getFile());
             }
            Collection<JarInput> jarInputs = input.getJarInputs();
         for (JarInput jarInput : jarInputs) {
                allJars.add(jarInput.getFile());
             }
     }
}

2.對收集到的.class檔案結合ASM框架進行分析處理

ASM是一個操作Java位元組碼的類別庫,通過ASM我們可以方便對.class檔案進行修改,

優先識別R類檔案,通過ClassVisitor訪問R.class檔案,讀取檔案中的靜態常量,進行臨時變數存盤:

@Overridepublic FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {    //R類中收集 public static final int 對應的變數  if (JDASMUtil.isPublic(access) && JDASMUtil.isStatic(access) &&JDASMUtil.isFinal(access) &&JDASMUtil.isInt(desc)) {       jdRstore.addInlineRField(className, name, value);      }      return super.visitField(access, name, desc, signature, value);}

非R類檔案,通過MethodVisitor識別到代碼中的R類參考,獲取參考對應的值,進行id值替換:

@Override
    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        if (opcode == Opcodes.GETSTATIC) {
            //owner:包名;name:具體變數名;value:R類變數對應的具體id值
            Object value = https://www.cnblogs.com/Jcloud/p/jdRstore.getRFieldValue(owner, name);
            if (value != null) {
              //呼叫該api實作值替換
                mv.visitLdcInsn(value);
                return;
            }
        }
        super.visitFieldInsn(opcode, owner, name, desc);
    }

*注:以上代碼僅為部分示意代碼,非正式插件代碼,

在業務模塊引入R類瘦身插件后,業務模塊功能可正常運行,且插件包大小均有3%~5%不同程度的減少,

公共資源R類ID行內

由于在京東android客戶端代碼中,更多的資源檔案集中在公共資源庫中,相對的公共庫生成的R類檔案也更大,對編譯后的apk包內容進行分析后,公共資源庫的R類檔案占比高達3%,

公共庫跟隨宿主一起打包,在宿主打包程序中引入R類瘦身插件,打包后的apk有明顯的減小,手機安裝apk后啟動首頁正常展示無問題,但在打開某些業務插件時,會有例外閃退現象,崩潰型別為R.x resource not found,對崩潰原因分析如下:業務插件代碼中使用了公共庫中的R類資源、插件打包流程獨立于宿主打包,在插件打包的程序中僅完成了業務模塊R類的行內,并沒有考慮到公共資源R類的行內,基于上述原因當宿主打包程序完成R類檔案洗掉瘦身后,我們在運行某業務插件的程序中,自然就會報公共資源R類找不到的問題從而產生崩潰,

為了解決這個問題一開始的方案設想是增加白名單機制,keep住所有被業務模塊使用的公共資源,但很快這個想法就被推翻,公共資源存在本身就是希望各個業務模塊直接參考這部分資源,而不是自己定義,如果keep住的話,必然有很大一部分的資源無法刪減,瘦身的效果會大打折扣,

既然保留的方案并不合適,那就將公共資源R類id也行內到代碼中去,前面提到京東是支持插件化的,整個插件化方案是基于aura平臺實作的,我們向aura團隊進行了咨詢,然后get到了新的方案切入點,

aura平臺在插件化的程序中已通過aapt2引入了公共資源id固定的能力,在該能力下,已定義的公共資源id會一直固定(各個業務插件中參考的公共資源id一致),且公共資源庫中已有的資源不可被其他模塊重復定義,否則會覆寫之前已定義好的資源,基于上述的結果和規則,我們對之前的R檔案瘦身gralde plugin功能進行完善,將公共資源的R類id 行內到專案中,

利用appt2的-stable-ids和-emit-ids兩個引數實作固化資源id的功能,并將將固化后的ids檔案命名為shared_res_public.xml存盤在公共資源庫中,業務插件依賴公共資源庫,在打包編譯的程序中aura會將shared_res_public.xml復制到業務工程臨時編譯檔案夾intermediates下的指定位置并參與業務模塊的打包程序中,其檔案內容格式如下:

修改R檔案瘦身gradle plugin 代碼,從指定位置讀取并識別這部分公共資源,按照<name,id>的形式進行變數存盤,并在后續程序中對業務模塊中的公共資源部分進行id替換,

public Map<String, String> parse() throws Exception {
        if (in == null) {
            return null;
        }
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(in);
        Element rootElement = doc.getDocumentElement();
        NodeList list = rootElement.getChildNodes();
        ......
        return resNode;
    }
}

R類資源id行內部分代碼如下:

public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        if (opcode == Opcodes.GETSTATIC) {
            //優先從業務模塊R類資源中查找
            Object value = https://www.cnblogs.com/Jcloud/p/jdRstore.getRFieldValue(owner, name);
            if (value != null) {
                mv.visitLdcInsn(value);
                return;
            }
           //從公共R類資源中查找
            value = getPublicRFileValue(name);
            if (value != null) {
                mv.visitLdcInsn(value);
                return;
            }
        }
        super.visitFieldInsn(opcode, owner, name, desc);
    }

該方案完善后,結合商詳業務插件進行了驗證,在商詳及宿主均完成R檔案行內瘦身后,商詳模塊業務功能可正常使用,無例外現象,

考慮到R檔案行內瘦身gradle plugin是在打包編譯階段引入的,我們也統計了一下引入該插件以后對打包時長的影響,資料如下:

結合資料來看,引入R檔案瘦身插件后對整體打包時長并無顯著影響,

至此,基于京東商城探索的插件化工程R檔案瘦身gradle plugin就開發完成,目前已在部分業務插件模塊進行了線上驗證,在功能上線以后我們也及時的進行了崩潰觀測以及用戶反饋的跟進,暫無例外問題,當然圍繞R檔案瘦身縮減包體積這個目的,開發人員有各種各樣的技術方案,上述方案不一定適用于所有的客戶端開發體系,另外后續也將圍繞包瘦身這一常態事務建設一系列的相關工具,介入作業當中的各個階段,高效、有效的控制包體積的增長,如大家在瘦身方面有相關建議和想法也歡迎大家來一起討論,

參考文章:

Gradle Plugin:

https://docs.gradle.org/current/userguide/custom_plugins.html

Gradle Transform:

https://developer.android.com/reference/tools/gradle-api/7.0/com/android/build/api/transform/Transform

APK 構建流程:

https://developer.android.com/studio/build/index.html?hl=zh-cn#build-process

作者:耿蕾 田創新

來源:京東云開發者社區

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/555147.html

標籤:Android

上一篇:直播回顧|走進元服務,攜手小強停車探索鴻蒙新流量陣地

下一篇:返回列表

標籤雲
其他(160963) Python(38226) JavaScript(25495) Java(18235) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7251) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4592) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1966) Web開發(1951) C++(1939) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 插件化工程R檔案瘦身技術方案 | 京東云技術團隊

    隨著業務的發展及版本迭代,客戶端工程中不斷增加新的業務邏輯、引入新的資源,隨之而來的問題就是安裝包體積變大,前期各個業務模塊通過無用資源刪減、大圖壓碩訓轉上云、AB實驗業務邏輯下線或其他手段在降低包體積上取得了一定的成果。 ......

    uj5u.com 2023-06-14 10:16:29 more
  • 直播回顧|走進元服務,攜手小強停車探索鴻蒙新流量陣地

    本期直播《“元”來如此,“服務”直達——揭秘鴻蒙新流量陣地》聚焦**元服務**的**商業流量價值**,介紹元服務提供的服務直達和卡片動態變化等**輕量化服務**。網約停車旗艦平臺小強停車做客直播間,分享小強停車在HarmonyOS生態中,如何通過元服務為廣大用戶帶來更加便捷易用的線上預約停車體驗。快 ......

    uj5u.com 2023-06-13 09:08:45 more
  • 直播回顧|走進元服務,攜手小強停車探索鴻蒙新流量陣地

    本期直播《“元”來如此,“服務”直達——揭秘鴻蒙新流量陣地》聚焦**元服務**的**商業流量價值**,介紹元服務提供的服務直達和卡片動態變化等**輕量化服務**。網約停車旗艦平臺小強停車做客直播間,分享小強停車在HarmonyOS生態中,如何通過元服務為廣大用戶帶來更加便捷易用的線上預約停車體驗。快 ......

    uj5u.com 2023-06-13 08:58:07 more
  • 【有獎調研】互聯網新型社交,華為在找“元服務搭子”,快來集合!

    “聊技術無話不談,一起來吹吹元服務!暢聊你對元服務的想法,說不定,你就能撬動元服務的爆發增長!” 元服務(即原子化服務)是華為“輕量化”服務的新物種,可提供全新的服務和互動方式,讓應用化繁為簡,讓服務觸手可及!基于鴻蒙萬能卡片,元服務可實作應用功能在桌面“永遠打開”,實作智能推薦、服務直達! 而在元 ......

    uj5u.com 2023-06-13 08:36:02 more
  • 【有獎調研】互聯網新型社交,華為在找“元服務搭子”,快來集合!

    “聊技術無話不談,一起來吹吹元服務!暢聊你對元服務的想法,說不定,你就能撬動元服務的爆發增長!” 元服務(即原子化服務)是華為“輕量化”服務的新物種,可提供全新的服務和互動方式,讓應用化繁為簡,讓服務觸手可及!基于鴻蒙萬能卡片,元服務可實作應用功能在桌面“永遠打開”,實作智能推薦、服務直達! 而在元 ......

    uj5u.com 2023-06-13 08:35:37 more
  • 構建docker編譯AOSP

    ## 配置AOSP docker編譯環境 ### 1.安裝docker ``` curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh ``` 參考:[github](https://github.com/docker/ ......

    uj5u.com 2023-06-09 08:24:52 more
  • 構建docker編譯AOSP

    ## 配置AOSP docker編譯環境 ### 1.安裝docker ``` curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh ``` 參考:[github](https://github.com/docker/ ......

    uj5u.com 2023-06-09 08:24:32 more
  • [Kotlin Tutorials 22] 協程中的例外處理

    # 協程中的例外處理 ![coroutine exception handling](https://img2023.cnblogs.com/blog/325852/202306/325852-20230608084235670-684439238.png) ## Parent-Child關系 如果 ......

    uj5u.com 2023-06-08 09:45:50 more
  • [Kotlin Tutorials 21] 協程的取消

    # 協程的取消 本文討論協程的取消, 以及實作時可能會碰到的幾個問題. ![coroutine cancellation](https://img2023.cnblogs.com/blog/325852/202306/325852-20230607235812812-279507376.png) 本 ......

    uj5u.com 2023-06-08 09:45:40 more
  • 怎樣將PWA和小程式融合?

    PWA代表“漸進式網路應用”(Progressive Web Application)。它是一種結合了網頁和移動應用程式功能的技術概念。PWA旨在提供類似于原生應用程式的用戶體驗,包括離線訪問、推送通知、后臺同步等功能,同時又具有網頁的優勢,如跨平臺、無需下載安裝等。 PWA使用現代的Web技術來創 ......

    uj5u.com 2023-06-08 09:45:33 more