日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

aapt2 生成资源 public flag 标记

發布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 aapt2 生成资源 public flag 标记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

之前寫過一篇aapt2適配之資源id固定,該文章介紹了如何使用aapt2固定資源id,其實這篇文章是對該文章的一點補充,主要介紹如何在固定id的同時,將該資源進行導出,打上public標記,供其他資源進行引用。整個問題的解決方案斷斷續續差不多思考了一個來月,現將解決方法簡單介紹一下。

從aapt2資源id固定說起

首先來回顧一下aapt2如何將資源id符號表導出,使用–emit-ids參數指定導出文件即可

12345android { aaptOptions { additionalParameters "--emit-ids", "${project.file('public.txt')}"}}

以及符號表導出后,如何使用導出的符號表進行資源id的固定,使用–stable-ids參數,指定導入文件即可

12345android { aaptOptions { additionalParameters "--stable-ids", "${project.file('public.txt')}"}}

執行./gradlew assembleDebug編譯后,看下產出的resources_debug.ap_文件,看下arsc中對應的資源是否打上了PUBLIC導出標記

1aapt2 dump ./app/build/intermediates/res/resources-debug.ap_

執行命令后輸出如下內容

123456789Package name=io.github.lizhangqu.aapt2 id=7f type layout id=1 entryCount=1spec resource 0x7f010090 io.github.lizhangqu.aapt2:layout/activity_main() (file) res/layout/activity_main.xml type style id=2 entryCount=1spec resource 0x7f020080 io.github.lizhangqu.aapt2:style/AppTheme() (style)

可以看出,arsc中對應的資源并沒有打上PUBLIC標記。問題就出在這,我們明明指定了導入資源符號表,為什么沒有PUBLIC標記呢?

從aapt public.xml說起

回顧一下aapt是如何添加PUBLIC標記的,其實這事和android gradle plugin有關,在android gradle plugin 1.3以下的版本,我們可以直接往src/main/res/values目錄下添加public.xml文件,該文件會自動參與編譯,但是不幸的是在1.3以上版本,所有public類型的資源全都會被android gradle plugin過濾掉,因此正常的途徑我們是無法讓public.xml參與編譯的。

所以在aapt的時候,我們在在mergeResources任務最后,將public.xml文件拷貝到build目錄下merge完畢的res目錄下,即build/intermediates/res/merged/目錄,然后gradle在執行processResources任務時會將我們拷貝進去的文件與其他資源文件一同參與編譯,于是就可以為那些資源打上PUBLIC標記,并進行id的固定。

那么在aapt2中,由于aapt2提供了一種固定資源id的方式,最開始,個人以為這種方式和aapt的public.xml作用是一樣的,在經過嚴格的測試后發現其實并不是想象的那么簡單,它只是固定資源id的一種方式,但是并不包括添加資源PUBLIC標記,因此aapt2的public.txt不等于aapt的public.xml,在aapt2中如果要添加PUBLIC標記,其實還是得另尋其他途徑。

通過查看aapt2的源碼發現,資源是否屬于public類型,在資源文件compile為flat文件的時候就已經決定了,也就是說必須主動聲明public類型的資源,這個資源才會被打上PUBLIC標記,所以問題就變成了和aapt一樣,只要在mergeResource任務的最后,將public.xml拷貝到mergeResource任務的輸出文件夾即可。但是有一個棘手的問題,就是aapt2的mergeResource任務的輸出文件不是原始資源文件,而是經過編譯后的flat文件,所以最終的問題變成了如何將public.xml文件編譯為flat文件。

由于最開始沒有想到這種方式,一直糾結于怎么修改aapt2的源碼,來達到添加PUBLIC標記的效果,后來不經意間想到這種方式,發現之前想復雜了。這個問題其實非常簡單,只需要獲取aapt2可執行文件,自己調用一下compile命令,傳遞相關參數執行下資源編譯步驟,然后將編譯后的文件拷貝到mergeResource任務的輸出文件夾,參考這篇文章aapt2資源compile過程可以獲得aapt2資源編譯的命令行參數及使用方式。那么gradle代碼怎么實現呢?也很簡單

12345678910111213141516171819202122232425262728project.afterEvaluate { def android = project.getExtensions().findByName('android')android.getApplicationVariants().all { def variant -> def mergeResourceTask = project.tasks.findByName("merge${variant.getName().capitalize()}Resources") if (mergeResourceTask) {mergeResourceTask.doLast { def variantData = variant.getMetaClass().getProperty(variant, 'variantData') def mBuildToolInfo = variantData.getScope().getGlobalScope().getAndroidBuilder().getTargetInfo().getBuildTools() //buildTools下的所有可執行文件都在這個map里Map<BuildToolInfo.PathId, String> mPaths = mBuildToolInfo.getMetaClass().getProperty(mBuildToolInfo, "mPaths") as Map<BuildToolInfo.PathId, String>project.exec(new Action<ExecSpec>() {@Overridevoid execute(ExecSpec execSpec) { //拼接aapt2 compile參數execSpec.executable "${mPaths.get(BuildToolInfo.PathId.AAPT2)}"execSpec.args("compile")execSpec.args("--legacy")execSpec.args("-o")execSpec.args("${mergeResourceTask.outputDir}")execSpec.args("${project.file('src/main/res/values/public.xml')}")}})}}}}

然后將public.xml放在app/src/main/res/values/目錄下,編譯一下,然后用aapt2 dump命令查看一下產出的arsc文件

123456789Package name=io.github.lizhangqu.aapt2 id=7f type layout id=1 entryCount=1spec resource 0x7f010090 io.github.lizhangqu.aapt2:layout/activity_main PUBLIC() (file) res/layout/activity_main.xml type style id=2 entryCount=1spec resource 0x7f020080 io.github.lizhangqu.aapt2:style/AppTheme PUBLIC() (style)

可以看到,資源名后面多了一個PUBLIC字符,也就是說該資源被打上PUBLIC標記了,對于其他項目中的資源,可以在aapt2鏈接的時候直接使用-I參數引用該arsc,就和android.jar的引用是一樣的,使用@包名:資源類型/資源名進行引用,如

1@io.github.lizhangqu.aapt2:style/AppTheme

到這里為止,其實已經解決了PUBLIC標記的問題,但是還不夠完美

自動轉換public.txt文件為public.xml文件

上面雖然解決了PUBLIC標記的問題,但是還得自己維護app/src/main/res/values/public.xml文件,由于aapt2自己會導出一份public.txt文件,因此可以利用這份文件,在編譯過程中將其自動轉為public.xml文件,就不需要我們自己維護public.xml文件。

這個問題其實說簡單也很簡單,只是有一些細節需要處理

  • public.txt中存在styleable類型資源,public.xml中不存在,因此轉換過程中如果遇到styleable類型,需要忽略
  • vector矢量圖資源如果存在內部資源,也需要忽略,在aapt2中,它的名字是以$開頭,然后是主資源名,緊跟著__數字遞增索引,這些資源外部是無法引用到的,只需要固定id,不需要添加PUBLIC標記,并且$符號在public.xml中是非法的,因此忽略它即可
  • 由于aapt2有資源id的固定方式,因此轉換過程中可直接丟掉id,簡單聲明即可
  • aapt2編譯的public.xml文件的上級目錄必須是values文件夾,否則編譯過程會報非法路徑

所以整個過程就變成了讀取public.txt的每一行,正則匹配資源類型,資源名,資源id,如果是內部資源和styleable類型資源,直接無視,否則寫入public.xml文件,寫入只需要寫入資源類型和資源名即可,資源id按需選擇,所以最終的代碼抽象為如下函數

12345678910111213141516171819202122232425262728293031323334353637383940414243/** * 轉換publicTxt為publicXml */@SuppressWarnings("GrMethodMayBeStatic")void convertPublicTxtToPublicXml(File publicTxtFile, File publicXmlFile, boolean withId) { if (publicTxtFile == null || publicXmlFile == null || !publicTxtFile.exists() || !publicTxtFile.isFile()) { throw new GradleException("publicTxtFile ${publicTxtFile} is not exist or not a file")}GFileUtils.deleteQuietly(publicXmlFile)GFileUtils.mkdirs(publicXmlFile.getParentFile())GFileUtils.touch(publicXmlFile) project.logger.info "convert publicTxtFile ${publicTxtFile} to publicXmlFile ${publicXmlFile}"publicXmlFile.append("<!-- AUTO-GENERATED FILE. DO NOT MODIFY -->")publicXmlFile.append("\n")publicXmlFile.append("<resources>")publicXmlFile.append("\n")Pattern linePattern = Pattern.compile(".*?:(.*?)/(.*?)\\s+=\\s+(.*?)")publicTxtFile.eachLine {def line ->Matcher matcher = linePattern.matcher(line) if (matcher.matches() && matcher.groupCount() == 3) {String resType = matcher.group(1)String resName = matcher.group(2) if (resName.startsWith('$')) { project.logger.info "ignore to public res ${resName} because it's a nested resource"} else if (resType.equalsIgnoreCase("styleable")) { project.logger.info "ignore to public res ${resName} because it's a styleable resource"} else { if (withId) {publicXmlFile.append("\t<public type=\"${resType}\" name=\"${resName}\" id=\"${matcher.group(3)}\" />\n")} else {publicXmlFile.append("\t<public type=\"${resType}\" name=\"${resName}\" />\n")}}}}publicXmlFile.append("</resources>")}

最開始的那段代碼就可以優化為如下代碼

12345678910111213141516171819202122232425262728293031project.afterEvaluate { def android = project.getExtensions().findByName('android')android.getApplicationVariants().all { def variant -> def mergeResourceTask = project.tasks.findByName("merge${variant.getName().capitalize()}Resources") if (mergeResourceTask) {mergeResourceTask.doLast { //目標轉換文件,注意public.xml上級目錄必須帶values目錄,否則aapt2執行時會報非法文件路徑 File publicXmlFile = new File(project.buildDir, "intermediates/res/public/${variant.getDirName()}/values/public.xml") //轉換public.txt文件為publicXml文件convertPublicTxtToPublicXml(project.file('public.txt'), publicXmlFile, false) def variantData = variant.getMetaClass().getProperty(variant, 'variantData') def mBuildToolInfo = variantData.getScope().getGlobalScope().getAndroidBuilder().getTargetInfo().getBuildTools()Map<BuildToolInfo.PathId, String> mPaths = mBuildToolInfo.getMetaClass().getProperty(mBuildToolInfo, "mPaths") as Map<BuildToolInfo.PathId, String> project.exec(new Action<ExecSpec>() {@Override void execute(ExecSpec execSpec) {execSpec.executable "${mPaths.get(BuildToolInfo.PathId.AAPT2)}"execSpec.args("compile")execSpec.args("--legacy")execSpec.args("-o")execSpec.args("${mergeResourceTask.outputDir}")execSpec.args("${publicXmlFile}")}})}}}}

總結

有時候,解決問題一種思路行不通可以換一種方式,說不定就會柳暗花明呢!

http://fucknmb.com/2018/02/02/aapt2-%E7%94%9F%E6%88%90%E8%B5%84%E6%BA%90public-flag%E6%A0%87%E8%AE%B0/

總結

以上是生活随笔為你收集整理的aapt2 生成资源 public flag 标记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。