Android中如何修改編譯的資源ID值(預設值是0x7F...可以隨意改成0x02~0x7E)

來源:互聯網
上載者:User

Android中如何修改編譯的資源ID值(預設值是0x7F...可以隨意改成0x02~0x7E)
一、技術準備

今天我們來看一下如何修改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是可以定製的,雖然有的同學可能現在用不到這個功能,但是我相信遲早有一天你一定會用到的,所以只要記住有這個技術方案就好了。

  

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.