推薦先仔細(xì)看一下這個(gè):https://developer.android.com/studio/build/shrink-code.html
先介紹一下問題背景:項(xiàng)目中有一些調(diào)用了Resources.getIdentifier()動(dòng)態(tài)反射資源的場(chǎng)景,為了防止資源在構(gòu)建時(shí)被shrink,所以我們?cè)谙胗袥]有辦法保證keep住我們需要反射的資源,同時(shí)shrink無用的資源。
首先明確我們最終的目的是apk的瘦身
然后我們來做一些測(cè)試哈:
我們先打一個(gè)googleRelease的包:size:36.1M
然后我們打開shrinkResources true然后再打包:36.1M
大小沒變?應(yīng)該是調(diào)用getIdentifier()的原因。這時(shí)我們拆包看確實(shí)我們通過反射拿到的資源都在

這時(shí)我們把項(xiàng)目中的調(diào)用的getIdentifier()方法都注釋掉。再打包。包大小仍然沒有變化:仍然是36.1M
如果我們沒有調(diào)用getIdentifie()按理來說應(yīng)該是把bubble..給shrink掉了,但事實(shí)沒有。(這里不排除第三方庫(kù)調(diào)用了getIdentifier())這個(gè)怎么搞呢。)
項(xiàng)目比較復(fù)雜,我們寫一個(gè)簡(jiǎn)單的Demo來測(cè)試。。到底什么時(shí)候會(huì)被shrink掉資源。。。來總結(jié)一下規(guī)律
寫這樣一個(gè)Demo
場(chǎng)景一:
shrinkResources true
getResources().getIdentifier("p4","drawable", "com.example.zhangtao.shrinkapp");
//先調(diào)用getResources().getIdentifier()正常傳入res名稱p4。

毫無疑問資源不會(huì)被順掉。。
場(chǎng)景二:
shrinkResources true
//下面是需要運(yùn)行時(shí)才知道結(jié)果 是不是p4誰都不知道
getResources().getIdentifier(builder.append(getPackageName()).append(getClassLoader().getClass().getName()).toString(),"drawable", "com.example.zhangtao.shrinkapp");
//發(fā)現(xiàn)原本很大的資源不見了
但是我們發(fā)現(xiàn)了這樣一個(gè)東西???

為什么會(huì)有這么一個(gè)文件呢,這是干啥用的。。
具體的說明我也沒找到,但是我找到了這個(gè),在output/mapping/release/resources.txt 下這個(gè)文件記錄了shrink resource的信息。有這么一行。。。

意思就是“報(bào)告長(zhǎng)官,發(fā)現(xiàn)了一個(gè)沒有被用到的資源,文件名是p4,我們把它換成了一個(gè)空殼子文件也叫p4”。
想一想這個(gè)叫shrink,意思是收縮,壓縮。是資源壓縮,是沒有刪除的意思。。。所以說以后注意了,沒用到的資源最好還是刪了,不要太指望shrink,他會(huì)留下“空殼子”
場(chǎng)景三:
shrinkResources true
//我們就寫這么一句話,涉及到了一個(gè)常量"test_pic_p4" 這里我們改個(gè)名,因?yàn)榻Y(jié)果比較意外。。
findViewById(R.id.image).setId(Integer.parseInt("test_pic_p4"));
//不調(diào)用getIdentifier
這個(gè)結(jié)果有點(diǎn)意外。。。拆包看看。。

我們發(fā)現(xiàn)了test_pic_p4的原圖。這個(gè)東西有點(diǎn)坑啊,資源是否被順好像和getIdentifier沒啥關(guān)系呀。。。這里只能猜了,可能sdk調(diào)用了。。無從驗(yàn)證。。
查看一下resources.txt,會(huì)發(fā)現(xiàn)這么一行。。

意思是:drawable test_pic_p4 匹配到了string pool的常量“test_pic_p4”
這個(gè)稍微有一點(diǎn)坑人啊。。。再來一個(gè)更想不到的。。。
場(chǎng)景四:
shrinkResources true
//這里使用資源名的子串:
findViewById(R.id.image).setId(Integer.parseInt("test_pic"));
//不調(diào)用getIdentifier

還是能看到。。。查看resources.txt 發(fā)現(xiàn):

意思是:drawable test_pic_p4 匹配到了string pool的常量“test_pic”
這還做的是模糊匹配呢、、、可以理解google為了保證安全,防止資源找不到,盡可能shink安全的資源,任何有可能用到的資源都會(huì)保留。
說到這我就感覺shrinkResources有點(diǎn)不太靠譜了,很多沒用到的資源都可能匹配到導(dǎo)致不被壓縮,所以資源最好還是自己刪。
場(chǎng)景五:
啟用嚴(yán)格匹配:
定義文件在 res/raw/keep.xml 內(nèi)容啟用嚴(yán)格引用檢查:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"/>
//引用字符串
findViewById(R.id.image).setId(Integer.parseInt("test_pic_p4"));
//調(diào)用getIdentifier 但傳入的東西不知道肯定不是test_pic_p4,但編譯器不知道。
getResources().getIdentifier(builder.append(getPackageName()).append(getClassLoader().getClass().getName()).toString(),"drawable", "com.example.zhangtao.shrinkapp");

圖片被壓縮了。
如果這么寫:getResources().getIdentifier("test_pic_p4","drawable", "com.example.zhangtao.shrinkapp");

這個(gè)東西也會(huì)被壓縮。。。再回去讀一下文檔對(duì)strict的說明。
這些是默認(rèn)情況下啟用的安全壓縮模式的示例。但您可以停用這一“有備無患”處理方式,并指定資源壓縮器只保留其確定已使用的資源
也就是說如果你調(diào)用了getIdentifier動(dòng)態(tài)加載資源 肯定是不能有tools:shrinkMode="strict",反射的資源肯定是找不到的,除非keep。(上面也有說就算沒有tools:shrinkMode="strict",也有可能找不到)
下面來總結(jié)規(guī)律:(個(gè)人總結(jié))
1.模糊匹配使得shrinkResources壓縮的力度不大。
shrinkResources true 很大的可能 沒使用的資源沒被壓縮(會(huì)有各種各樣的匹配原則),包大小很可能沒多大變化。如果調(diào)用getIdentifier可能找到資源,也可能找不到資源。
2.建議確定不用的資源手動(dòng)刪除shrinkResources true會(huì)留下空殼子。
3.嚴(yán)格模式下,只有正常使用的資源會(huì)保留,其他的都會(huì)被壓縮。
4.shrinkResources true不管是否嚴(yán)格模式,不能使用getIdentifier(除非你配置keep住所有要使用的資源)。
5.關(guān)于混淆的原則

我們?cè)赟hrinkResourcesTransform 中可以找到這樣一段(ResourceUsageAnalyzer)
策略是
.9 圖會(huì)用33的一個(gè).9圖替換掉。
png會(huì)用11的png替換掉
部分
既然有了上面的結(jié)論,我們也就知道為什么我們開啟了shrinkResources true不管是否調(diào)用getIdentifier 包大小都沒有明顯變化。
下面來說推薦方案:
推薦方案一:
shrinkResources false; 手動(dòng)刪除項(xiàng)目中所有的沒用到的資源。
推薦方案二:
shrinkResources true;啟用嚴(yán)格模式,把所有反射的資源都放到keep.xml下(這樣包里會(huì)有shrink殼子)。
對(duì)于方案一和二,都可以正常使用getIdentifier。
其實(shí)如果方案二實(shí)現(xiàn)了也就相當(dāng)于把方案一做了,有一個(gè)轉(zhuǎn)換方法。方案二做完后,查看resources.txt就可以看到所有被shrink的資源,這樣再把每一個(gè)資源刪除,就變成方案一了、