Android插件混淆解決方法以及MultiDex的配置

最近在公司做的工作都是插件化相關(guān)不脯,所以看了很多插件化的框架李丰。整個(gè)插件化的方案現(xiàn)在是比較成熟的,怎樣處理ClassLoader廉嚼,怎么替換Activity生命周期,怎么去處理Receiver和Service倒戏,幾個(gè)主流的框架基本上都是大同小異怠噪。我們團(tuán)隊(duì)選用了AndroidPluginFramework這個(gè)框架,具體的BenchMark其實(shí)在很多框架下面都可以看到杜跷。如何選取還是取決于自身需求傍念,在插件化這塊其實(shí)主流的需求一般是兩種:

  1. 完全獨(dú)立的插件矫夷。就是給一個(gè)APK和宿主沒有關(guān)系,宿主可以不安裝的情況下調(diào)起這個(gè)APK憋槐,讓用戶無感知双藕。
  2. 非獨(dú)立插件。這個(gè)其實(shí)是大部分公司的需求阳仔,就是隨著公司業(yè)務(wù)的發(fā)展忧陪,客戶端承載的業(yè)務(wù)越來越多,這個(gè)時(shí)候無論是從團(tuán)隊(duì)合作的角度還是動(dòng)態(tài)化的角度近范,都希望各個(gè)業(yè)務(wù)之間解耦嘶摊,發(fā)布能更加獨(dú)立和動(dòng)態(tài)。這種模式下评矩,一般會(huì)抽出一個(gè)公共庫叶堆,給各個(gè)組件提供基本功能,比如手淘還未開源的Atlas的結(jié)構(gòu)斥杜。
    借用一張架構(gòu)圖虱颗,公共庫抽象出中間件那個(gè)模塊,提供給各個(gè)組件基本的能力


    atlas.png
    atlas.png

AndroidPluginFramework這個(gè)框架是支持這兩種場景的果录,但我們實(shí)際業(yè)務(wù)場景是第二種,非獨(dú)立插件咐熙。公共庫中包含里基本的網(wǎng)絡(luò)弱恒,緩存,以及UI框架棋恼。當(dāng)然獨(dú)立插件最后出來也是獨(dú)立的APK返弹。基本背景介紹完了爪飘,接下來開始講講本文的主題吧义起,關(guān)于插件化時(shí)代碼混淆的問題。這個(gè)問題應(yīng)該很容易想到师崎,我們公共庫中提供出去給插件使用的類應(yīng)該只在宿主中有一份默终,宿主打包的時(shí)候把公共庫打包到宿主的APK中,插件只應(yīng)該在編譯過程中用到犁罩,gradle中以provide的方式依賴這些代碼齐蔽,比如我們工程中mobilebase是公共依賴庫

 provide files(project(':mobilebase').getBuildDir().absolutePath + '/intermediates/bundles/release/classes.jar')
    provide files(project(':mobilebase').getBuildDir().absolutePath + '/outputs/rClasses.jar')

當(dāng)然就會(huì)遇到一個(gè)問題是宿主在打release包的時(shí)候,會(huì)混淆mobilebase類床估,此時(shí)插件是不知道混淆的規(guī)則的含滴,所以當(dāng)插件想去調(diào)用公共庫時(shí)就會(huì)ClassNotFound或者method不對(duì)。如何解決這個(gè)問題丐巫,有兩種思路

  1. 完全不混淆mobilebase谈况,keep住mobilebase中的所有東西勺美。這個(gè)方案適用于你的公共庫夠薄的情況,比如你各個(gè)組件之間公用的東西很少碑韵,那適用這個(gè)方案赡茸。
  2. 使用相同的混淆規(guī)則。這個(gè)其實(shí)聽上去相對(duì)合理一點(diǎn)的方案泼诱,宿主和插件使用相同的混淆的規(guī)則坛掠,理所當(dāng)然能解決上面的問題。

我們其實(shí)公共庫里的東西還是有點(diǎn)多的治筒,所以準(zhǔn)備用第二種方案屉栓。
Proguard在開啟混淆時(shí),會(huì)在app的 ****/build/outpust/mapping**** 目錄下生成四個(gè)文件

dump.txt
說明 APK 中所有類文件的內(nèi)部結(jié)構(gòu)耸袜。
mapping.txt
提供原始與混淆過的類友多、方法和字段名稱之間的轉(zhuǎn)換。
seeds.txt
列出未進(jìn)行混淆的類和成員堤框。
usage.txt
列出從 APK 移除的代碼域滥。

其中mapping文件是混淆的規(guī)則,故我們只需要把這個(gè)文件用到插件的混淆配置中即可蜈抓。所以拷貝這個(gè)文件到插件的目錄启绰,在插件的proguard-rules中添加

-applymapping mapping.txt

表示復(fù)用mappting,但由于混淆規(guī)則中的很多類插件是沒有的沟使,所以會(huì)有很多的Warning,所以我們配置一下ignore掉這些w委可,最終插件的混淆配置如下

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-ignorewarnings
-printseeds

-applymapping mapping.txt

OK,到這兒本以為能混淆的配置可以了腊嗡,但運(yùn)行時(shí)發(fā)現(xiàn)着倾,插件中調(diào)用到公共庫的地方并沒有被正常混淆燕少,還是找不到混淆后的方法卡者。調(diào)研了發(fā)現(xiàn)provide的包gradle不會(huì)去混淆。客们。崇决。

接下來就得去折騰multidex了

首先現(xiàn)在的問題是,我們不能把公共庫打包到插件的apk中底挫,但是以provide方式依賴又會(huì)出現(xiàn)無法混淆的問題嗽桩。看了下AndroidPluginFramework官方提供的混淆建議(作者表示也沒有試過)

 具體方法:
        1凄敢、開啟混淆編譯宿主碌冶,保留mapping文件
        2、將插件的build.gradle文件中的provided配置換成compile涝缝, 因?yàn)閜rovided方式提供的包不會(huì)被混淆
        3扑庞、在插件的混淆配置中apply編譯宿主時(shí)產(chǎn)生的mapping文件譬重。
        4、接著在插件編譯腳本中開啟multdex編譯罐氨。并配置multdex的mainlist臀规,使得原先所有provided的包的class被打入到副dex中。
           這樣插件編譯完成后栅隐,會(huì)有2個(gè)dex塔嬉,1個(gè)是插件自己需要的代碼,1個(gè)是原先provided后來改成了compile的那些包租悄。
        5谨究、再將這個(gè)原provided的包形成的dex,也就是副dex從apk中刪除泣棋,再對(duì)插件apk重新簽名胶哲。

簡單來說就是先全部混淆,再利用multidex把之前provide的類全部打到第二個(gè)dex中潭辈,再刪除第二個(gè)dex鸯屿,再重新簽名得到混淆后的插件APK。

那就去試用一下multidex這個(gè)官方的拆包的庫把敢,至于這個(gè)multidex的原理以及大量的坑網(wǎng)上都能搜到很多的分析文章寄摆,美團(tuán)也有很多技術(shù)分析文章
我說說我在實(shí)施這個(gè)過程中的坑吧,就是如何實(shí)現(xiàn)把指定的class打包到Class2.dex中修赞。因?yàn)槲覀冃枰巡寮念惔虻街鱠ex婶恼,其余provide的類打包到第二個(gè)dex中。
這個(gè)問題網(wǎng)上最多的答案是在你插件的build.gradle中插入如下腳本

afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        def listFile = project.rootDir.absolutePath + '/plugintest/maindexlist.txt'
        println "root dir:" + project.rootDir.absolutePath
        println "dex task found:" + dx.name
        if (dx.additionalParameters == null) {
            dx.additionalParameters = []
        }
        dx.additionalParameters += '--multi-dex'
        dx.additionalParameters += '--main-dex-list=' + listFile
        dx.additionalParameters += '--minimal-main-dex'
        dx.additionalParameters += '--set-max-idx-number=20000'
    }
}

這段腳本的意思是當(dāng)你插件的gradle的task graph掃描完成的時(shí)候榔组,在dexXXX的任務(wù)中插入幾個(gè)參數(shù)熙尉,

  1. --main-dex-list= 這個(gè)是一個(gè)txt文件指明你想哪些類打包到主dex
  2. --minimal-main-dex 最小化主dex联逻,保證主dex中只有上面參數(shù)指定的類
  3. --set-max-idx-number 每個(gè)dex中最多的方法數(shù)(不太確定搓扯,大概是這個(gè)意思,默認(rèn)值65535)

網(wǎng)上的答案大部分是這段腳本包归,但你發(fā)現(xiàn)********并不會(huì)生效********锨推。因?yàn)閐exXXXDebug這個(gè)任務(wù)只在gradle1.5以下才有,之后就被隱藏了公壤。
我現(xiàn)在版本是2.2應(yīng)該怎么配置换可?

android{
dexOptions {
        additionalParameters += '--main-dex-list=maindexlist.txt'
        additionalParameters += '--minimal-main-dex'
        additionalParameters += '--set-max-idx-number=20000'
    }
}

在Android配置中添加這段即可,當(dāng)然gradle1.5之后開始提供更多的第三方接口厦幅,所以也可以嘗試使用
https://github.com/ceabie/DexKnifePlugin 這個(gè)分包插件來完成沾鳄。
配置上以上混淆配置和multidex后,再打包插件确憨,發(fā)現(xiàn)主dex中并沒有我們預(yù)料的那些類译荞,反而少了很多瓤的,反編譯來看,貌似我們配置到maindexlist.txt中的類被混淆了吞歼。

網(wǎng)上查到原來maindexlist.txt中需要配置混淆之后的類名圈膏,這個(gè)就坑了,實(shí)用性大減篙骡,分包實(shí)在混淆之后稽坤,所以流程上來說確實(shí)要配置混淆之后的類名。為了簡化這個(gè)過程糯俗,我最終選擇keep住插件中的所有類尿褪,只會(huì)混淆公共依賴類。

那插件中的類怎么才能全部寫到maindexlist中叶骨,當(dāng)然寫個(gè)腳本掃描一下代碼目錄即可

#!/bin/bash
SPATH=`pwd`'/src/main/java'

function walk()
{
  for file in `ls $1`
  do
    local path=$1"/"$file
    if [ -d $path ]
     then
      echo "DIR $path"
      walk $path
    else
      a=${path#*/plugintest/src/main/java/}
      echo ${a/.java/.class}>>maindexlist.txt
    fi
  done
}
 
echo $SPATH
walk $SPATH

上面得腳本放到插件根目錄下茫多,打包前跑一遍便會(huì)自動(dòng)生成maindexlist.txt.
完成上述之后即可正確混淆,分包也正確,混淆規(guī)則和宿主一致忽刽。

截下來就是對(duì)于打出來的插件包天揖,刪除class2.dex并且重新打包簽名

這也應(yīng)該是由腳本來完成的工作,由于對(duì)于jar命令暫時(shí)發(fā)現(xiàn)只有update這個(gè)操作跪帝,所以比較low的方式創(chuàng)建了一個(gè)叫class2.dex的空文件用于覆蓋打包后apk中的class2.dex今膊。
腳本如下

#!/bin/bash

KEYSTORE_NAME=your key file
KEYSTORE_ALIAS=your key alias
KEYSTORE_STOREPASS=your key store password
KEYSTORE_KEYPASS=your key password

INPUT_APK=./build/outputs/apk/plugintest-release.apk
CLASS2=classes2.dex
META_INF=./META-INF

UNSIGNED=./build/outputs/apk/plugintest-release.apk
SIGNED=./build/outputs/apk/plugintest-release_resign.apk
OPT=./build/outputs/apk/plugintest-release_resign_align.apk


jar -uf $UNSIGNED $CLASS2
jar -uf $UNSIGNED $META_INF
echo Replace OK!

jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore $KEYSTORE_NAME -storepass $KEYSTORE_STOREPASS -keypass $KEYSTORE_KEYPASS -signedjar $SIGNED $UNSIGNED $KEYSTORE_ALIAS
echo Signe OK!

rm -r $OPT
zipalign 4 $SIGNED $OPT
echo Zipalign ok!

#rm -r $UNSIGNED
#rm -r $SIGNED
echo Operate OK!

注意一點(diǎn)是必須要從之前的apk中拷貝出META_INF下面的幾個(gè)文件,才能完成正常的重新簽名伞剑,否則插件lib在校驗(yàn)簽名時(shí)會(huì)報(bào)失敯呋!:


WechatIMG88.jpeg
WechatIMG88.jpeg

以上就是整個(gè)插件打包和混淆的過程,由于剛接觸插件化不久黎泣,如果有更合理的混淆方案恕刘,請(qǐng)告知一下,搞這塊還是挺蛋疼的抒倚,記錄一下褐着!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市托呕,隨后出現(xiàn)的幾起案子含蓉,更是在濱河造成了極大的恐慌,老刑警劉巖项郊,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馅扣,死亡現(xiàn)場離奇詭異,居然都是意外死亡着降,警方通過查閱死者的電腦和手機(jī)差油,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來任洞,“玉大人蓄喇,你說我怎么就攤上這事食绿。” “怎么了公罕?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵器紧,是天一觀的道長。 經(jīng)常有香客問我楼眷,道長铲汪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任罐柳,我火速辦了婚禮掌腰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘张吉。我一直安慰自己齿梁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布肮蛹。 她就那樣靜靜地躺著勺择,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伦忠。 梳的紋絲不亂的頭發(fā)上省核,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音昆码,去河邊找鬼气忠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赋咽,可吹牛的內(nèi)容都是我干的旧噪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼脓匿,長吁一口氣:“原來是場噩夢啊……” “哼淘钟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起亦镶,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤日月,失蹤者是張志新(化名)和其女友劉穎袱瓮,沒想到半個(gè)月后缤骨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尺借,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年绊起,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燎斩。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虱歪,死狀恐怖蜂绎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笋鄙,我是刑警寧澤师枣,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站萧落,受9級(jí)特大地震影響践美,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜找岖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一陨倡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧许布,春花似錦兴革、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袁余,卻和暖如春解阅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泌霍。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工货抄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朱转。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓蟹地,卻偏偏與公主長得像,于是被迫代替她去往敵國和親藤为。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怪与,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 最近幾年移動(dòng)開發(fā)業(yè)界興起了「 插件化技術(shù) 」的旋風(fēng),各個(gè)大廠都推出了自己的插件化框架缅疟,各種開源框架都評(píng)價(jià)自身功能優(yōu)...
    斜杠時(shí)光閱讀 3,943評(píng)論 1 36
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,735評(píng)論 25 707
  • 引言 如果App引用的庫太多分别,方法數(shù)超過65536后無法編譯。這是因?yàn)閱蝹€(gè)dex里面不能有超過65536個(gè)方法存淫。為...
    喜歡丶下雨天閱讀 10,142評(píng)論 2 20
  • 動(dòng)態(tài)加載技術(shù) 介紹 在程序運(yùn)行的時(shí)候耘斩,加載一些程序自身原本不存在的可執(zhí)行文件并運(yùn)行這些文件里的代碼邏輯。 動(dòng)態(tài)調(diào)用...
    冰點(diǎn)k閱讀 3,911評(píng)論 1 11
  • 上個(gè)世紀(jì)七八十年代桅咆,在我國農(nóng)村有一種現(xiàn)象括授,現(xiàn)在聽來會(huì)覺得匪夷所思,但在當(dāng)時(shí)卻是習(xí)以為常的,黎慧荚虚、黎智以及蘇敏正是這...
    梓博兩三事閱讀 1,008評(píng)論 11 12