直接在apk中添加资源的研究
原文?http://blog.votzone.com/2018/05/12/apk-merge.html
之前接手過一個(gè)sdk的開發(fā)工作,在開發(fā)過程中有一個(gè)很重要的點(diǎn)就是盡量使用代碼來創(chuàng)建控件,資源文件最好放到assets目錄下,如果必須使用res資源,需要通過?getResources().getIdentifier("activity_splash","layout", getPackageName())?這種方式來獲取資源id,而不能直接通過R文件獲取。
今天就來研究一下這個(gè)問題。
一、lib項(xiàng)目中r文件中資源唯一標(biāo)志為static變量
一般的app項(xiàng)目中自動(dòng)生成的R文件為常量,而在library項(xiàng)目中為變量。根據(jù)Android官方文檔,在android 14 之后添加的這一特性,之前編譯后的lib項(xiàng)目中是常量,之后的為static 變量。 目的是為了在資源沖突時(shí)能夠修改資源唯一值。 如圖在library項(xiàng)目中,自動(dòng)生成的R文件如下
有一個(gè)需要關(guān)注的點(diǎn)是R文件是java 代碼,在build時(shí)會(huì)生成.class文件并添加到dex中。 因?yàn)镽文件中的常量值僅僅受編譯器控制,在lib發(fā)布之后添加到j(luò)ar包中的.class并不會(huì)受到當(dāng)前編譯器的影響。而通常lib發(fā)布之后要給第三方使用。
看一下反編譯后的apk
反編譯后查看mylib 下對(duì)應(yīng)的R文件smali代碼,可見其值又被設(shè)置為final(常量)了。 因此我們可以知道編譯器在生成apk時(shí)雖然沒有l(wèi)ib的java代碼可以重置和修改,但是在將jar轉(zhuǎn)化為dex時(shí)可以轉(zhuǎn)換為常量。 轉(zhuǎn)化為常量后并不影響其使用, 如圖在lib中使用layout資源的反編譯代碼
我們可以看出使用靜態(tài)常量可以在生成apk時(shí)動(dòng)態(tài)修改其指定的值。
如果是常量,編譯后v0的值將直接使用常量值,這樣修改R.class文件中的值將沒有意義。例如在app中反編譯后代碼如下:
二、從反編譯后代結(jié)構(gòu)中查看資源對(duì)應(yīng)問題
可以看到,反編譯后的代碼與我們寫的java代碼基本是一一對(duì)應(yīng)的,那么問題來了,Android是怎么通過一個(gè)id值來找到需要的資源呢?通過分析Android源碼我們當(dāng)然可以找出過程,但是分析apk反編譯后的結(jié)構(gòu)可以給我們更直接的思路。
在res/values/文件夾下有個(gè)public.xml文件,其中每行有三對(duì)值,分別為type、name、id?通過分析我們可以直接得出結(jié)論:?public.xml中的對(duì)應(yīng)關(guān)系直接關(guān)系到哪個(gè)id找那個(gè)資源。
三、添加資源
Unity3d和cocos2d引擎有自己一套方式來添加資源id,假如我們自己搞一個(gè)游戲,又想為其添加一些java代碼和資源該怎么操作?
根據(jù)之前的分析我們可以設(shè)計(jì)如下測(cè)試方案
根據(jù)如上實(shí)驗(yàn)我們可以確定這樣的操作是可行的。測(cè)試流程如下:
需要的資源:
- targetapp- 目標(biāo)app, 在這里代表游戲
- mergelib- 要將其中包含資源的代碼合并進(jìn)去
mergelib 中對(duì)資源通過getIdentifier()的方式使用: 例如設(shè)置啟動(dòng)頁Activity中的ContentView的設(shè)置
setContentView(id);
我們的目標(biāo):為targetapp添加一個(gè)啟動(dòng)頁,啟動(dòng)頁代碼在mergelib中編寫。
執(zhí)行流程
修改targetapp中AndroidManifest.xml文件?
1) 將SplashActivity 的聲明添加進(jìn)去?
2) 修改啟動(dòng)Activity
復(fù)制需要的代碼進(jìn)入targetapp:?
復(fù)制mergelib中SplashActivity的代碼并修改啟動(dòng)MainActivity的啟動(dòng)代碼 如圖
復(fù)制資源
1) 將res/anim 下alpha.xml復(fù)制到target
2) 將res/layout 下 spalsh.xml復(fù)制到target 下
修改ids?
將 res/values/ids.xml 中多出來的行復(fù)制到 對(duì)應(yīng)ids.xml文件中
如圖, 本例中僅有一個(gè)id 即ImageView的id, 因此將該行復(fù)制到targetapp 中對(duì)應(yīng)的ids.xml文件中即可 位置不重要
修改public.xml文件 mergelib Splash中用到了?anim,?layout,?id, 并且間接用到了?mipmap,因此這些對(duì)應(yīng)的值都需要添加到targetapp。觀察public.xml的文件結(jié)構(gòu),可以發(fā)現(xiàn)如下特點(diǎn):?
1) 所有同類型(type相同)的id連續(xù)?
2) 同類型的id 前4字節(jié)相同, 如下圖 anim 的前四字節(jié)0x7f01 與 attr 不同
3) 所有id唯一?
根據(jù)以上三個(gè)特點(diǎn),我們將多出來的id添加到target為了保證唯一且方便修改,我們做了如下替換(右側(cè)為target)
一切就緒,編譯并安裝
四、工具化處理public.xml的替換過程
上述手動(dòng)測(cè)試僅僅只有一個(gè)資源id的情況,假如我們加入了一個(gè)support包或者其他一些包含資源的包,那么資源數(shù)量將會(huì)增加到幾百個(gè),這樣的話手動(dòng)添加肯定是不行的,我們需要一個(gè)腳本工具來實(shí)現(xiàn)。
腳本接收兩個(gè)public.xml格式的文本,并輸出一個(gè)合并版本。 其中targetapp中的id值是不可變得,在遇到id沖突時(shí),我們改變mergelib的public.xml。
1) 復(fù)制mergelib的public文件為mergeid.xml?
2) 復(fù)制target app 的public 并命名為oriid.xml
3) 將mergeid.xml 和oriid.xml 放到RIDreset.py同目錄下, 運(yùn)行py腳本
?
4) 出現(xiàn) oriid.xml_ 文件, 即生成的合并文件
案例及腳本
https://github.com/votzone/DroidCode/tree/master/VotAndroid/Mergeapp
轉(zhuǎn)載于:https://www.cnblogs.com/fog2012/p/apk_merge.html
總結(jié)
以上是生活随笔為你收集整理的直接在apk中添加资源的研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Zabbix linux agent 安
- 下一篇: chromium之histogram.h