Android二次打包之重新生成R文件

安卓經(jīng)常需要打多個(gè)渠道包,當(dāng)二次打包時(shí),資源ID會(huì)重新生成泞坦。如果代碼中有第三方SDK通過直接引用R文件的方式來(lái)獲取資源ID,就會(huì)出現(xiàn)資源ID不匹配的問題砖顷。
本文主要介紹解決此類問題的三種方法贰锁。

一 背景

為什么要二次打包

大家都知道,國(guó)內(nèi)安卓渠道眾多滤蝠,游戲想要上架渠道就要接入他們的sdk豌熄。這對(duì)于游戲開發(fā)商(CP)來(lái)說(shuō)是一個(gè)不小的工作量。

通過接入我們的聚合SDK物咳,CP只需要提供一個(gè)母包锣险,然后使用我們的打包工具就可以打出幾十個(gè)渠道包,非常的高效。

二次打包的基本原理

打包工具的基本原理就是通過反編譯囱持,把SDK的代碼和資源文件打入到游戲母包夯接,然后重新打包簽名,生成對(duì)應(yīng)的渠道包纷妆。

當(dāng)然這其中會(huì)涉及到許多細(xì)節(jié)方面的東西盔几,不是本文重點(diǎn),就不展開了掩幢。

二次打包之資源文件ID

打包時(shí)逊拍,會(huì)生成兩個(gè)文件。

一個(gè)是resources.arsc.里面包含了所有資源文件的索引ID

示例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <public type="drawable" name="btn_login" id="2130837507" />
    <public type="layout" name="paydialog" id="2130903044" />
</resources>

另外一個(gè)是R文件际邻,里面提供了代碼中引用的name和resources.arsc中對(duì)應(yīng)的ID

示例:

public final class R {
        public static final class drawable {
            public static final int btn_login = 2130837507;
    }
    public static final class layout {
        public static final int paydialog = 2130903049;
    }
}

這樣我們通過引用R文件就可以找到對(duì)應(yīng)的資源芯丧。

但是,正如前面所說(shuō)世曾,resources.arsc和R.java是打包的時(shí)候才會(huì)生成缨恒。因此,二次打包會(huì)重新生成資源ID轮听,如果我們通過直接引用R文件的方式來(lái)尋找資源骗露,就會(huì)出現(xiàn)問題。

二 解決方法

1. 常規(guī)操作:動(dòng)態(tài)獲取資源ID

這是最簡(jiǎn)單最通用的解決方法血巍。

比如說(shuō)我們可以通過如下方式獲取一個(gè)布局文件的ID:

public static int getLayoutId(Context context,String paramString) {
   mContext = context;
    return getResourceId("layout", paramString);
}

protected static int getResourceId(String paramString1, String paramString2) {
        return mContext.getResources().getIdentifier(paramString2, paramString1,
                mContext.getPackageName());
    }
  

2. 奇技淫巧:替換掉第三方SDK中R文件的引用

可是萧锉,如果第三方SDK就是通過R文件直接引用資源ID怎么辦?

比如說(shuō)述寡,他們引用了com.corpA.sdk.R柿隙,重新打包之后,會(huì)在我們的包名(com.corpB.demo)文件夾下生成新的R文件鲫凶。因此禀崖,我們可以讓SDK直接引用我們包名文件夾下的R文件。

具體咋做呢螟炫?

2.1 SDK代碼反編譯為smali文件

使用apktool將第三方SDK的所有java代碼反編譯為smali文件波附。

2.2 查找替換所有R文件的引用

將smali文件中所有com/corpA/sdk/R替換為com/corp/demo/R。這樣不恭,重新打包后叶雹,第三方SDK在運(yùn)行時(shí)會(huì)去我們包名下最新的R文件中尋找資源ID

這種方法簡(jiǎn)單粗暴财饥,不需要手動(dòng)生成R文件换吧,也不需要更改SDK中原來(lái)的R文件,直接通過打包腳本在反編譯階段改掉第三方SDK的代碼钥星。

3. 曲線救國(guó):直接修改第三方SDK中引用的R文件

凡是總有例外沾瓦,不是所有的SDK都能反編譯的。比如有些SDK會(huì)將Java代碼打成一個(gè)加密的Dex文件,放到asset文件夾下面贯莺。這種加密的Dex文件我們是無(wú)法反編譯的风喇,所以上面的方法2就行不通了。

由于無(wú)法反編譯缕探,這個(gè)Dex里面的內(nèi)容對(duì)于我們就是一個(gè)黑盒魂莫,我們無(wú)法知道它里面是否有直接引用R文件的情況,以及引用了哪里的R文件爹耗。

這個(gè)其實(shí)沒有很好地辦法耙考,只能先按照方法2來(lái)處理掉可以反編譯的Java代碼,然后打出一個(gè)包來(lái)進(jìn)行測(cè)試潭兽。如果加密Dex中有直接引用到R文件倦始,那么就會(huì)出現(xiàn)崩潰,從日志中我們就可以找到引用R文件的位置山卦。然后我們就可以修改指定位置的R文件啦鞋邑。

過程有些繁瑣,下面詳細(xì)說(shuō)明:

3.1 找到引用R文件的位置

通過崩潰日志账蓉,我們可以找到Dex文件中所引用的R文件的位置枚碗,假設(shè)該R文件的路徑是com.corpA.sdk.R

3.2 使用aapt手動(dòng)生成R文件

aapt(Android Asset Packaging Tool)是安卓sdk中負(fù)責(zé)將資源文件和代碼進(jìn)行打包的工具√拊常基本上所有的打包工具都是在aapt的基礎(chǔ)上進(jìn)行封裝和修改的视译,打包的過程比較繁瑣,總結(jié)下來(lái)大概有如下幾個(gè)步驟:

  1. 通過aapt工具归敬,生成R.java和.arsc文件
  2. 通過aidl工具酷含,把a(bǔ)idl文件打包成java文件
  3. 通過javac工具,將Java文件編譯成.class文件
  4. 通過dx工具汪茧,把class文件和第三方j(luò)ar打包成dex文件
  5. 通過apkbuilder工具椅亚,把dex和資源文件打包成apk文件
  6. 通過JarSinger工具,對(duì)apk進(jìn)行簽名
  7. 通過zipAlign工具舱污,對(duì)apk做對(duì)齊處理

我們要改的就是第一步:aapt生成R文件呀舔!步驟如下

  1. 反編譯游戲母包和接入渠道SDK的插件包,將代碼和資源文件合并

  2. 修改AndroidManifest.xml中的包名為最終渠道包的包名

  3. 使用Android sdk的aapt工具手動(dòng)生成R文件

    前提是需要首先安裝Andorid sdk相應(yīng)的工具扩灯,并配置好環(huán)境變量媚赖。aapt指令比較復(fù)雜,核心就是下面這個(gè):

os.getenv('ANDROID_BUILD_TOOL') + "/aapt package -f -m -J " + temp_path + " -S " + res_path + " -I " + os.getenv('ANDROID_PLATFORM') + "/android.jar -M " + manifest_path

其中temp_path是生成的R文件輸出路徑珠插;res_path是res文件夾的路徑惧磺;manifest_path是manifest的路徑

3.3 將R文件轉(zhuǎn)換為Smali文件

這個(gè)過程大概4個(gè)步驟: java --> class --> jar --> dex --> smali

示例:

    # 1. build_r_class
    r_java_path = temp_path + os.sep + package_name.replace('.', os.sep) + os.sep + "R.java"
    cmd_build_r_class = "javac -source 1.6 -target 1.6 " + r_java_path
    execCmd(cmd_build_r_class)
    file_util.deleteFile(r_java_path)

    # 2. generate jar
    os.chdir("/data/soft/jenkins/workspace/Packaging_Tools-All" + os.sep + temp_path)
    cmd_generate_r_jar = "jar cvf " + "r.jar " + "com"
    execCmd(cmd_generate_r_jar)

    # 3. generate dex
    cmd_generate_dex = os.getenv('ANDROID_BUILD_TOOL') + "/dx --dex --output=" + "r.dex " +         "r.jar"
    execCmd(cmd_generate_dex)

    # 4. generate smali
    cmd_generate_smali = "java -jar " + "/data/soft/jenkins/workspace/Packaging_Tools-All" +         os.sep + "baksmali-2.0.jar " + "r.dex"
    execCmd(cmd_generate_smali)

3.4 拷貝smali文件到對(duì)應(yīng)位置

  1. 我們要拷貝一份smali文件到包名下對(duì)應(yīng)目錄。

  2. 然后同樣拷貝到3.1步驟中R文件的路徑(com.corpA.sdk.R)捻撑。同時(shí)磨隘,我們需要修改smali中R文件的引用為com.corpA.sdk.R缤底。這樣才能保持更Asset下面的Dex文件中的引用一致。

至此番捂,關(guān)于R文件的處理已經(jīng)完成个唧,然后重新打包就可以啦~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市设预,隨后出現(xiàn)的幾起案子徙歼,更是在濱河造成了極大的恐慌,老刑警劉巖鳖枕,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鲁沥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡耕魄,警方通過查閱死者的電腦和手機(jī)画恰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吸奴,“玉大人允扇,你說(shuō)我怎么就攤上這事≡虬拢” “怎么了考润?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)读处。 經(jīng)常有香客問我糊治,道長(zhǎng),這世上最難降的妖魔是什么罚舱? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任井辜,我火速辦了婚禮,結(jié)果婚禮上管闷,老公的妹妹穿的比我還像新娘粥脚。我一直安慰自己,他們只是感情好包个,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布刷允。 她就那樣靜靜地躺著,像睡著了一般碧囊。 火紅的嫁衣襯著肌膚如雪树灶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天糯而,我揣著相機(jī)與錄音天通,去河邊找鬼。 笑死歧蒋,一個(gè)胖子當(dāng)著我的面吹牛土砂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谜洽,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼萝映,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了阐虚?” 一聲冷哼從身側(cè)響起序臂,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎实束,沒想到半個(gè)月后奥秆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咸灿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年构订,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片避矢。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悼瘾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出审胸,到底是詐尸還是另有隱情亥宿,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布砂沛,位于F島的核電站烫扼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏碍庵。R本人自食惡果不足惜映企,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望静浴。 院中可真熱鬧卑吭,春花似錦、人聲如沸马绝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)富稻。三九已至掷邦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椭赋,已是汗流浹背抚岗。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哪怔,地道東北人宣蔚。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓向抢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胚委。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挟鸠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345