編輯:關於android開發
今天我們來看一下如何修改Android中編譯時的資源Id的值,在講解這內容之前,我們需要先了解一下Android中的資源編譯之後的結構和編譯過程,這裡就不多說了,這篇文章中,介紹了如何解析Android中編譯之後的resource.arsc文件,這裡就介紹了Android中資源文件編譯之後的類型和格式,其實Android中資源編譯之後,會產生一個R文件,所有的資源ID都是存儲在這個文件中的的,默認我們看到所有的ID都有一個共同的特點,就是他們都是0x7F開頭的,其實這個0x7F是包的ID值,我們在在解析resource.arsc文章中提到一點,Android中的id值其實是一個int類型,他的值由三部分組成:PackageId+TypeId+EntryId
PackageId:是包的Id值,Android中如果是第三方應用的話,這個值默認就是0x7F,系統應用的話就是0x01,具體我們可以後面看aapt源碼得知,他占用兩個字節。
TypeId:是資源的類型Id值,一般Android中有這幾個類型:attr,drawable,layout,dimen,string,style等,而且這些類型的值是從1開始逐漸遞增的,而且順序不能改變,attr=0x01,drawable=0x02....他占用兩個字節。
EntryId:是在具體的類型下資源實體的id值,從0開始,依次遞增,他占用四個字節。
既然我們了解了Android中的資源Id的結構,下面我們來說說我們遇到的問題:
1、在Android項目中偶爾會出現依賴第三方庫包,出現資源ID(packageId+typeId+ItemValue)發生沖突的問題(網上有很多解決方案,不一一列舉,如public 限定等)。那麼對於我們自己提供的庫包,如果能指定其包的命令空間(默認是從127=0x7F開始),特別考慮mutiDex的情況,自定義修改package ID顯得意義重大。
2、我們在開發Android中插件技術的時候,為了防止插件工程中的資源Id和宿主工程中的資源Id不沖突,也是需要去修改一下插件中編譯之後的資源Id值,來減少沖突。
那麼上面就是我們遇到的問題,其實我們的解決方案很簡單,就是在編譯的時候修改資源Id值,給一個限定值。
我們之前講解了資源Id的組成結構,發現高兩個字節是代表PackageId的值,而且第三方app的默認值是0x7F,那麼我們能不能修改這個值呢?比如,插件1中的資源Id中的PackageId為0x30,插件2中的資源Id中的PackageId為0x31...這樣每個插件的資源就被劃分了一定的區域值,同時保證不要和主工程中的0x7F沖突即可,那麼這些值就可以從0x02~0x7E了,這個區間值我們都是可以使用的,為什麼0x01不能用呢?因為他是系統應用的呀,所以我們就有0x7E-0x02=124個區間,哈哈,聽著好興奮,那麼我們是否可以操作了呢?答案是可以的,我們知道Android中編譯資源用的是aapt命令,那麼我們就可以查看他的源碼來看看是否可以。
aapt命令是Android中提供的編譯apk的一個工具,所以源碼可以從 Android源碼目錄/tools/... 下面查看:
這個工具的源碼還是不復雜的,沒多少文件,當然入口肯定找main啥的關鍵字了,果然看到一個Main.cpp文件,打開查看,找到入口函數main,這裡我們可以看到,他對輸入參數做了判斷:
這裡main函數有點長,我們直接看最後的處理函數:
這裡有一個handleCommand函數,這裡就是主要處理命令的功能:
這裡有好多個函數,但是我們這裡需要關注的是doPackage函數,他是打出包的關鍵,但是這時候我們發現全局搜這個函數,找不到,那麼這個函數肯定是被引用的,源碼中查找具體函數,
腦補一下:
這裡因為是Window系統,不想是Linux系統,可以直接使用find+grep就可以快速的查找到包含指定內容的文件了,但是Windows中提供了可視化的文件搜索,但是他默認在搜索的時候,只是搜索文件名,不搜索包含的內容,所以需要設置一下,可以到文件夾選項中設置:
這時候我們可以在tools目錄下搜索了:
這時候看到了,我們搜到了三個文件,Main.cpp可以不用看了,因為已經看過了,那麼就在Command.cpp裡面了:
這裡我們往下面看:
這裡有一個方法,而且我們看注釋,這裡就是編譯的核心函數:buildResources,我們在全局搜這個函數,沒找到,那麼我們還是到整個目錄下去搜:
搜到了,在Resource.cpp中:
這裡看到,一個packgeType字段,這個就是包類型,這裡有三個類型:共享的,系統的,第三方
突然發現這個似乎和PackageId的值有關系,我們接著往下看:
在這裡,用到了packageType,而且有一個重要的類型ResourceTable,這個就是資源索引表,和ResId有映射關系的數據結構,所以我們查看他的定義:在ResourceTable.cpp中
我擦,果然,看到結果了,這裡看到了有三個值,0x00,0x01,0x7F。說明我們找到核心的地方了。接著往下看:
這裡構建了一個Package,這裡傳入了packageId值的,好了,我們分析源碼就到這裡了,那麼下面我們來看一下源碼流程:
首先找到入口類:Main.cpp:main函數,解析參數,然後調用handleCommand函數處理參數對應的邏輯,我們看到了有一個函數doPackage,這裡就是處理編譯工作的。
然後就搜索到了Command.cpp:在他內部的doPackage函數中進行編譯工具的一個函數:buildResources函數,在全局搜索,發現了Resource.cpp:具體查看buildResources函數,發現這裡就是處理編譯工作,同時在這裡我們也看到了核心,構建ResourceTable的邏輯,在ResourceTable.cpp中,也是獲取PackageId的地方,到此我們就知道了大體的邏輯,那麼知道了邏輯,下面我們就來看看如何修改呢?
其實最好的方法是,能夠修改aapt源碼,添加一個參數,把我們想要編譯的PackageId作為輸入值,傳進來最好了,其實我們在看源碼的時候發現,有一個類型始終傳遞這,那就是Bundle類型,他是從Main.cpp中的main函數傳遞到了最後的buildResources函數中,那麼我們就可以把這個參數用Bundle進行攜帶。
既然知道了修改的思路,下面就是來修改源碼了:
第一步:修改Main.cpp中的main函數,獲取外部傳遞的PackageId值,然後存入到Bundle中
這裡我們使用的參數是:-apk-module
第二步:我們只需要在ResourceTable.cpp中的構造方法讀取這個值即可
到此,我們就修改完了,然後編譯,這裡編譯因為環境不同,所以這裡就不列出來如何編譯的了,本人使用VC6.0進行編譯的,得到了最終的修改之後的appt命令:aapt_win.exe
那麼既然上面我們那麼辛苦的修改了aapt命令,下面就可以大展生手的修改一下試一試了,用一個簡單的demo進行嘗試,不過這裡還有一個問題,就是這裡我們呀用ant腳本來編譯apk,因為我們需要修改aapt命令的路徑,換成編譯之後的aapt_win.exe,關於如何使用Ant腳本編譯apk,這裡就不做太多的解釋了,而且,我就是用這篇文章中的demo做案例的,就是改了一下編譯腳本:
修改aapt命令的路徑,用我們修改之後的命令
在編譯生成R文件的時候,添加參數:-apk-module
編譯resource.arsc也需要修改:
這裡全部修改成0x78,然後我們跑一個ant腳本:ant release
然後看一下R文件的內容:
哈哈哈,這裡我們看到修改成功了,不相信的話,我們可以用我們之前寫的一個工具:解析Resource.arsc文件的工具類,打印看一下結果(不了解的同學可以查看這篇文章:http://blog.csdn.net/jiangwei0910410003/article/details/50628894):
打印結果也是正常的,好吧,下面再來看一下,我們使用動態加載來加載這個apk(具體代碼這裡不粘貼了,首先我們可以查看log信息,我們在代碼中使用反射去獲取一個插件apk中的app_name字段的id值。
日志顯示的id是0x78050000,顯示的值也是正確的,這樣我們就讓插件的ResId的范圍區分了宿主工程中的0x7F,就不會出現資源沖突的問題了,看一下運行的效果:
好了,到這裡,我們就說完了本章的內容了。
aapt修改之後的源碼和工具下載地址:http://download.csdn.net/detail/jiangwei0910410003/9454867
1、學習了如何在Windows中查找源碼內容
2、學習了aapt編譯的整體流程
3、學會了修改編譯的資源id值
通過修改aapt源碼,來達到我們可以隨意定制編譯之後的resId值,解決我們在引用第三方包或者工程以及在開發插件化的時候遇到的資源id值沖突問題,所以這裡就記住一點,我們可以修改Android中編譯之後的資源ID值了。
結束了這篇文章,感覺收獲還是很多的,起碼我們知道Android中編譯之後的資源ID是可以定制的,雖然有的同學可能現在用不到這個功能,但是我相信遲早有一天你一定會用到的,所以只要記住有這個技術方案就好了。
Android開源項目分類匯總(六)工具庫 主要包括那些不錯的開發庫,包括依賴注入框架、圖片緩存、網絡相關、數據庫ORM建模、Android公共庫、Android 高版本
Android用戶界面的組成除了View外,還包括菜單和對話框,本節我們先來學
Android 用adb pull或push 拷貝手機文件到到電腦上,拷貝手機數據庫到電腦上,拷貝電腦數據庫到手機上,先說一下adb命令配置,如果遇到adb不是內部或外部
常見bug,bug 錯誤日志圖 被這弱智的錯誤吭了半個小時,項目本來好好的,然後因為改版加了很多東西,所以就超限了,一開始總是報下面那圖的錯,搞的我總以為