當前位置:
首頁 > 最新 > 直接在apk中添加資源的方法研究

直接在apk中添加資源的方法研究

之前接手過一個sdk的開發工作,在開發過程中有一個很重要的點就是盡量使用代碼來創建控制項,資源文件最好放到assets目錄下,如果必須使用res資源,需要通過getResources().getIdentifier("activity_splash","layout",getPackageName())這種方式來獲取資源id,而不能直接通過R文件獲取。

今天就來研究一下這個問題。

一、lib項目中r文件中資源唯一標誌為static變數

參考http://tools.android.com/tips/non-constant-fields

一般的app項目中自動生成的R文件為常量,而在library項目中為變數。根據Android官方文檔,在android 14 之後添加的這一特性,之前編譯後的lib項目中是常量,之後的為static 變數。

目的是為了在資源衝突時能夠修改資源唯一值

如圖在library項目中,自動生成的R文件如下

有一個需要關注的點是R文件是java 代碼,在build時會生成.class文件並添加到dex中。

因為R文件中的常量值僅僅受編譯器控制,在lib發布之後添加到jar包中的.class並不會受到當前編譯器的影響。而通常lib發布之後要給第三方使用。

看一下反編譯後的apk

反編譯後查看mylib 下對應的R文件smali代碼,可見其值又被設置為final(常量)了。

因此我們可以知道編譯器在生成apk時雖然沒有lib的java代碼可以重置和修改,但是在將jar轉化為dex時可以轉換為常量。

轉化為常量後並不影響其使用, 如圖在lib中使用layout資源的反編譯代碼

我們可以看出使用靜態常量可以在生成apk時動態修改其指定的值。

如果是常量,編譯後v0的值將直接使用常量值,這樣修改R.class文件中的值將沒有意義。例如在app中反編譯後代碼如下:

二、從反編譯後代結構中查看資源對應問題

可以看到,反編譯後的代碼與我們寫的java代碼基本是一一對應的,那麼問題來了,Android是怎麼通過一個id值來找到需要的資源呢?通過分析Android源碼我們當然可以找出過程,但是分析apk反編譯後的結構可以給我們更直接的思路。

在res/values/文件夾下有個public.xml文件,其中每行有三對值,分別為type、name、id

通過分析我們可以直接得出結論:

public.xml中的對應關係直接關係到哪個id找那個資源。

三、添加資源

Unity3d和cocos2d引擎有自己一套方式來添加資源id,假如我們自己搞一個遊戲,又想為其添加一些java代碼和資源該怎麼操作?

根據之前的分析我們可以設計如下測試方案

0、假定我們要添加代碼的apk為targetapp,我們編寫一個叫mergelib的apk,然後將mergelib中資源和代碼添加到targetapp中。

1、在編寫mergelib時,使用getResources().getIdentifier("activity_splash","layout",getPackageName())的方式獲取資源id;

2、通過apktool反編譯代碼,將資源文件複製到要加入的目標apk中;

3、將mergelib的public.xml文件中需要的資源項添加到targetapp中;

4、編譯並簽名測試。

根據如上實驗我們可以確定這樣的操作是可行的。

測試流程如下:

需要的資源:

targetapp- 目標app, 在這裡代表遊戲

mergelib- 要將其中包含資源的代碼合併進去

mergelib 中對資源通過getIdentifier()的方式使用:

例如設置splash的ContentView

intid= getResources().getIdentifier("activity_splash","layout", getPackageName());

setContentView(id);

目標,為targetapp添加一個啟動頁,啟動頁代碼在mergelib中編寫。

打包流程

1 修改targetapp中AndroidManifest.xml文件

1) 將SplashActivity 的聲明添加進去

2) 修改啟動Activity

2 複製需要的代碼進入target app

複製mergelib中SplashActivity的代碼並修改啟動MainActivity的啟動代碼 如圖

3 複製資源

1) 將res/anim 下alpha.xml複製到target

2) 將res/layout 下 spalsh.xml複製到target 下

4 修改ids

將 res/values/ids.xml 中多出來的行複製到 對應ids.xml文件中

如圖, 本例中僅有一個id 即ImageView的id, 因此將該行複製到targetapp 中對應的ids.xml文件中即可

位置不重要

5 修改public.xml文件

mergelib Splash中用到了 anim, layout, id, 並且間接用到了 mipmap,因此這些對應的值都需要添加到target

觀察public.xml的文件結構,可以發現如下特點:

1) 所有同類型(type相同)的id連續

2) 同類型的id 前4位元組相同, 如下圖 anim 的前四位元組0x7f01 與 attr 不同

3) 所有id唯一

根據以上三個特點,我們將多出來的id添加到target

為了保證唯一且方便修改,我們做了如下替換(右側為target)

6 一切就緒,編譯並安裝

四、工具化處理public.xml的替換過程

上述手動測試僅僅只有一個資源id的情況,假如我們加入了一個support包或者其他一些包含資源的包,那麼資源數量將會增加到幾百個,這樣的話手動添加肯定是不行的,我們需要一個腳本工具來實現。

腳本接收兩個public.xml格式的文本,並輸出一個合併版本。

其中targetapp中的id值是不可變得,在遇到id衝突時,我們改變mergelib的public.xml。

1) 複製mergelib的public文件為mergeid.xml

2) 複製target app 的public 並命名為oriid.xml

3) 將mergeid.xml 和oriid.xml 放到RIDreset.py同目錄下, 運行py腳本

如圖,直接雙擊py腳本,或者右鍵使用 python.exe 運行

4) 出現 oriid.xml_ 文件, 即生成的合併文件

案例及腳本

https://github.com/votzone/DroidCode/tree/master/VotAndroid/Mergeapp

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 VoT實驗室 的精彩文章:

TAG:VoT實驗室 |