亚洲欧美小说色综合小说一区_99在线热播精品免费_成人看片毛片免费播放器_影视一区二区_国产综合久久久久久鬼色_jizz蜜桃视频在线观看_日韩av网站免费在线_亚洲免费观看

當前位置:首頁 > 國內 >

環球要聞:插件化工程R文件瘦身技術方案 | 京東云技術團隊2023-06-13 11:28:23 | 編輯:admin | 來源:博客園

隨著業務的發展及版本迭代,客戶端工程中不斷增加新的業務邏輯、引入新的資源,隨之而來的問題就是安裝包體積變大,前期各個業務模塊通過無用資源刪減、大圖壓縮或轉上云、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文件。

@Overridepublic 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 inputs = invocation.getInputs();    for (TransformInput input : inputs) {        Collection directoryInputs = input.getDirectoryInputs();         for (DirectoryInput directoryInput : directoryInputs) {               allDirs.add(directoryInput.getFile());             }            Collection 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 = 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 代碼,從指定位置讀取并識別這部分公共資源,按照的形式進行變量存儲,并在后續過程中對業務模塊中的公共資源部分進行id替換。

public Map 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 = 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

作者:耿蕾 田創新

來源:京東云開發者社區

關鍵詞

上一篇:本草綱目秘方偏方大全減肥_本草綱目減肥配方 全球要聞 最后一頁下一篇:

推薦內容
中文字幕五月欧美| 亚洲欧美日韩国产一区二区三区| 欧美一区二区三区成人| 福利在线导航136| 一区二区久久| 在线精品视频免费观看| 动漫一区在线| 久久一二三区| 欧美日韩一区小说| 色香欲www7777综合网| 国产麻豆视频精品| 国产黄色高清在线| 精品国产不卡| 欧美性色视频在线| 欧美成人ⅴideosxxxxx| 成人午夜激情视频| 在线播放网站| 亚洲人成久久| 亚洲国产精久久久久久 | 第四色中文综合网| 国产精品免费av| 久久香蕉av| 久久久久久久网| 亚洲性图自拍| 久久综合九色综合欧美98| 福利片在线看| 成人av在线资源网站| 毛片在线播放a| 成人免费高清在线| 久久久久久国产精品免费无遮挡| 国产一区激情在线| 在线激情免费视频| 成人综合在线观看| 91精选在线| 欧美卡1卡2卡| 久久中文视频| 日韩精品久久久久久福利| 你懂的成人av| 国产911网站| 免费人成网站在线观看欧美高清| 一区二区三区视频国产日韩| 久久精品人人做人人爽电影蜜月| 日本一二三区视频免费高清| 久久精品123| 91短视频版在线观看www免费| 成人av在线播放网站| 国产精品蜜芽在线观看| 伊人一区二区三区| 欧美日韩一区二区三区在线电影| 日韩亚洲欧美一区| 99精品福利视频| 欧美欧美欧美| 国产精品不卡视频| 色婷婷精品视频| 国产一级黄色电影| 成人h版在线观看| 伦一区二区三区中文字幕v亚洲| 日韩欧美成人区| 欧美精品不卡| 在线观看男女av免费网址| 一区二区成人在线观看| 自拍偷拍精品| 青青国产在线| 午夜欧美一区二区三区在线播放| 日韩成人三级| 国产视频网址在线| 亚洲一区在线观看视频| 国产精品97| caoporn免费在线| 欧美日韩国产精选| 国模无码大尺度一区二区三区| 亚洲欧洲自拍| 精品香蕉一区二区三区| 国产亚洲欧美色| 亚洲免费观看高清完整版在线观| 另类av导航| 午夜伊人狠狠久久| 久久国产日韩| 国产一精品一av一免费爽爽| 羞羞视频网站在线免费观看| 欧美激情一区在线观看| 亚洲欧洲日韩| 欧美日韩视频免费观看| 亚洲欧美福利视频| 一级日本不卡的影视| 久久亚洲电影| 精品国产午夜肉伦伦影院| 邻家有女韩剧在线观看国语| 色综合网色综合| 国产高清在线精品| 国产一区二区三区四区五区传媒| 呦呦在线视频| 国产剧情演绎av| 色综合久久天天| gogogo免费视频观看亚洲一| 亚洲精品二区三区| 97人人做人人爽香蕉精品| 中文字幕在线观看第一页| 在线国产亚洲欧美| 91丨porny丨最新| 美女视频一区免费观看| 亚洲免费观看高清完整版在线观| 青青草原国产在线| 最新在线地址| 亚洲精品国产精品久久清纯直播| 一级精品视频在线观看宜春院| 国产一区二区剧情av在线| 91精品国产91久久久久久黑人| 亚洲欧洲日韩精品在线| 97精品资源在线观看| 可以直接在线观看的av| 日韩精品高清视频| 在线一区二区视频| 国产老妇另类xxxxx| 婷婷综合激情| 91精品麻豆| 精品国模一区二区三区欧美 | 亚洲电影一区二区| av成人老司机| 激情综合色丁香一区二区| 99re66热这里只有精品8| 欧美三级午夜理伦三级在线观看| 丁香六月综合| 鲁鲁在线中文| 麻豆av在线免费观看| 黄网页免费在线观看| 久草在线青青草| 亚州av电影免费在线观看| 嫩草懂你的影院| 成年在线播放小视频| 亚洲毛片在线观看.| 欧美成人女星排行榜| 欧美一区二区三区四区视频| 欧美日韩在线一区二区| 精品福利在线看| 在线中文字幕一区二区| 欧美日韩和欧美的一区二区| 欧美视频一区二区在线观看| 欧美性色黄大片手机版| 欧美日韩美少妇| 欧美成人一区二区三区在线观看| 91精品国产麻豆国产自产在线| 欧美一区二区网站| 欧美精品 国产精品| 精品久久人人做人人爱| 亚洲精品wwwww| 影音先锋导航| 在线观看av每日更新免费| 免费国产在线观看| 天堂8中文在线| av在线日韩| 成人知道污网站| 天天射综合网视频| 爽好久久久欧美精品| 国产99久久久国产精品潘金网站| 成人一区二区三区视频| 综合久久久久久久| 色妹子一区二区| 精品成人在线观看| 在线观看视频污| 高潮在线视频| 精品国产导航| 国产视频一区免费看| 国产精品综合在线视频| 国产精品久久久久三级| 欧美日韩中文字幕一区二区| 亚洲精品综合精品自拍| 黄色污网站在线免费观看| www.综合网.com| 欧美女王vk| 国产麻豆日韩欧美久久| 好吊成人免视频| 色老板在线视频| 中文字幕在线看片| 亚洲成av人片乱码色午夜| 丁香婷婷综合激情五月色| 91久久精品一区二区三| 中文字幕视频免费在线观看| 亚州一区二区三区| 亚洲国产专区校园欧美| 欧美激情中文字幕一区二区| 日韩美女主播在线视频一区二区三区| 搞黄视频免费在线观看| 欧洲精品一区| 成人国产视频在线观看| 欧美精品一二三区| 黄av在线免费观看| 青草国产精品| 国产亚洲一区二区三区四区| 精品国产人成亚洲区| 中文字幕乱码中文乱码51精品| 888久久久| 亚洲国产中文字幕| 酒色婷婷桃色成人免费av网| 老司机成人在线| 91在线国产观看| 丁香激情视频| 久久久久观看| 国产精品毛片大码女人| 最新av在线|