Gradle插件開發(fā) APK瘦身資源自定義7z壓縮

APK瘦身實(shí)戰(zhàn) 資源自定義7z壓縮

項(xiàng)目開發(fā)中,隨著業(yè)務(wù)的增長,常常需要在apk編譯階段對包代碼或是資源做一定的自定義修改,比如熱修復(fù),插件生成,無埋點(diǎn)統(tǒng)計(jì),渠道包生成等等。

但是公司項(xiàng)目業(yè)務(wù)開發(fā)人員基本上都很少接觸到相關(guān)技術(shù),這里以學(xué)習(xí)的態(tài)度,實(shí)現(xiàn)一套用7zip壓縮apk資源文件的gradle插件。

APK瘦身在行業(yè)內(nèi)已經(jīng)有很多成熟的開源技術(shù)體現(xiàn)方案,如美團(tuán)Android App包瘦身優(yōu)化實(shí)踐這篇博客中詳細(xì)的說明。
這里我們從資源壓縮入手,用7z工具,實(shí)現(xiàn)一套自己的資源壓縮gradle插件。

Gradle 插件簡單介紹

一般簡單的邏輯可以寫在build.gradle文件中,但是本著便于管理及重用,可以把插件獨(dú)立為項(xiàng)目。
獨(dú)立的gradle插件編譯后,我們可以發(fā)布到本地maven庫,或是jcenter中心,供其他項(xiàng)目引用。

項(xiàng)目結(jié)構(gòu)介紹

Gradle插件開發(fā)可以用android studio,也可以用IntelliJ。AS開發(fā)需要自己創(chuàng)建插件項(xiàng)目結(jié)構(gòu),而IntelliJ可以自動生成項(xiàng)目結(jié)構(gòu),但是用起來一些操作不是很順手。

如下圖,用IntelliJ New出來一個(gè)項(xiàng)目,按照引導(dǎo),即可生成我們的初始項(xiàng)目結(jié)構(gòu)及gradle-wrapper。

new

這里填寫插件 GroupId,ArtifactId,已經(jīng)插件版本Version。如果不確定,可以先隨意寫個(gè),隨后可以在項(xiàng)目中更改。

IntelliJ

標(biāo)準(zhǔn)的項(xiàng)目結(jié)構(gòu)如下:


項(xiàng)目結(jié)構(gòu)

如果用as開發(fā),需要手動創(chuàng)建如上結(jié)構(gòu)。從項(xiàng)目中可以看出支持groovy,和java的混合開發(fā),當(dāng)然從IntelliJ創(chuàng)建項(xiàng)目引導(dǎo)可以看出同時(shí)也是支持kotlin的。
每種語言都有各自的語言特點(diǎn),比如我們開發(fā)gradle插件,在與項(xiàng)目build編譯交互的地方用groovy開發(fā),業(yè)務(wù)的核心代碼用我們擅長的語言(java)開發(fā),這里使用7zip的地方就是用java封裝實(shí)現(xiàn)的。


項(xiàng)目結(jié)構(gòu)

resources文件夾比較重要,這里的文件標(biāo)明了插件的入口,及插件的引用名字。如果導(dǎo)出maven庫找不到自己插件引用,可以先檢查下這個(gè)文件結(jié)構(gòu)是否正確。

apk-shrink.properties

implementation-class=win.canking.gradle.ShrinkPlugin
apply plugin ‘a(chǎn)pk-shrink’

當(dāng)build.gradle 解析 apply plugin 時(shí),就會找到 win.canking.gradle.ShrinkPlugin, 開始執(zhí)行apply()方法

項(xiàng)目可能遇到的問題

1,發(fā)布本地maven,build.gradle配置如下

apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'maven'

group 'win.canking.gradle'
version '1.0.1'
archivesBaseName = 'apk-shrink'//ArtifactId默認(rèn)是項(xiàng)目module name

compileGroovy {
    sourceCompatibility = 1.7
    targetCompatibility = 1.7
    options.encoding = "UTF-8"
}

dependencies {
    compile gradleApi()
    compile localGroovy()
    compile 'com.android.tools.build:gradle:2.1.0'
}

uploadArchives {
    repositories.mavenDeployer {
        //文件發(fā)布到下面目錄
        repository(url: uri('../../ChargeHelperPro/gradleplugin/'))
    }
}


執(zhí)行如下代碼,可以生成本地的maven庫

gradlew -p {module name} clean build uploadArchives --info

2,不同JDK編譯問題,特別注意,需要配置sourceCompatibility

3, 引用找不到問題
先檢查導(dǎo)出目錄,是否生成了maven。目錄結(jié)構(gòu)如下:

maven

反編譯生成的jar包,查看打包是否正確。


JD-gui

APK資源自定義壓縮

APK包結(jié)構(gòu)

一個(gè)apk文件本質(zhì)上就是一個(gè)zip壓縮文件,我們可以用解壓縮工具解壓查看內(nèi)部結(jié)構(gòu)。

name desp
res 資源文件,該文件下資源都會映射到項(xiàng)目R文件中,生成引用ID
assets 靜態(tài)資源文件,訪問是需要用到 AssetManager
lib 第三包jar或者native so庫
META-INF 簽名信息,CERT.RSA CERT.SF MANIFRST.MF
AndroidManifest 項(xiàng)目清單文件,包含四大組件,包信息,權(quán)限等
classes.dex java的class文件通過dx工具生成的安卓執(zhí)行文件
resources.arsc 編譯后的二進(jìn)制資源文件,包含代碼對資源的引用關(guān)系

資源文件壓縮

aapt l -v xxx.apk
stored

從圖中看出apk中有些資源文件存儲方式為stored,是未經(jīng)壓縮狀態(tài),我們可以對apk再處理,通過高壓縮率的工具(7zip)壓縮文件,達(dá)到瘦身目的。

獲取7zip工具路徑

通過“which 7za“獲取PC上7zip工具目錄

        ProcessBuilder pb = new ProcessBuilder(new String[]{"which", "7za"});
        String sevenZipPath = "";
    try {
            Process process = pb.start();
            InputStreamReader ir = new InputStreamReader(process.getInputStream());
            LineNumberReader input = new LineNumberReader(ir);
            String tmp;
            while ((tmp = input.readLine()) != null) {
                if (tmp.endsWith(File.separator + "7za")) {
                    sevenZipPath = tmp;
                    Log.a("Shrink", "7zip path:" + sevenZipPath);
                }
            }
            process.waitFor();
            process.destroy();
        } catch (Exception e) {
            Log.e("Error", "no shrink" + e.getMessage());
            return;
        }

定義要壓縮的文件類型:

 public class CompressInfo {
    public Set<String> compressFilesPattern;

    private CompressInfo() {
        this.compressFilesPattern = new HashSet<>();
    }

    public static CompressInfo init() {
        CompressInfo info = new CompressInfo();
        info.compressFilesPattern.add(".png");
        info.compressFilesPattern.add(".jpg");
        info.compressFilesPattern.add(".JPG");
        info.compressFilesPattern.add(".jpeg");
        info.compressFilesPattern.add(".gif");
        info.compressFilesPattern.add(".arsc");
        return info;
    }
  }

調(diào)用7za進(jìn)行壓縮目標(biāo)文件

    private static void do7zip(String srcDirPath, String outZipPath, String sevenZipPath)
            throws IOException, InterruptedException {
        String srcFilesPath = new File(srcDirPath).getAbsolutePath() + File.separator + "*";
        outZipPath = new File(outZipPath).getAbsolutePath();

        ProcessBuilder pb = new ProcessBuilder(new String[]{sevenZipPath, "a", "-tzip", outZipPath, srcFilesPath, "-mx9"});
        Process process = pb.start();

        InputStreamReader ir = new InputStreamReader(process.getInputStream());
        LineNumberReader input = new LineNumberReader(ir);
        String line;
        while ((line = input.readLine()) != null) {
            Log.d(input.getLineNumber() + ":" + line);
        }
        process.waitFor();
        process.destroy();
    }

指定task來執(zhí)行壓縮任務(wù)

    @Override
    void apply(Project project) {
        Log.d("shrink apply")
        if (project.getPlugins().hasPlugin(AppPlugin)) {

            def config = project.extensions.create(SHRINK_CONFIG, ShrinkExtension)
            project.afterEvaluate {
                project.tasks.matching {
                    println it.name
                    it.name.startsWith('packageRelease')
                } each {
                    t ->
                        t.doLast {
                            if (config.enable) {
                                Log.d("shrink start...")
                                GradleMrg.do7zipCompressApk(config.apkPath)
                                Log.d("shrink EDN")
                            }
                        }
                }
            }
        }
    }

注:此Demo依賴項(xiàng)目的編譯流程,需要在自己項(xiàng)目中build.gradle中配置相關(guān)壓縮參數(shù):

shrinkConfig {
    enable = true
    apkPath = '/PATH/App-release.apk'//可通過代碼方法自動獲取
}

也可以自定義一個(gè)單獨(dú)的task,不依賴編譯流程。

壓縮效果對比:

shrink after

shrink before

本案例源碼以提交到GitHub,歡迎交流學(xué)習(xí)及star。


歡迎轉(zhuǎn)載,請標(biāo)明出處:常興E站 canking.win

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評論 2 375

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,675評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,799評論 18 139
  • 小的時(shí)候,讀徐志摩的詩,僅僅停留在語文教材上,那時(shí)候,只是淺顯的覺得他才華橫溢,溫文儒雅,除此之外,腦海中對...
    散落星河的記憶閱讀 231評論 0 0
  • 271 落葉 不知秋的傷 顧自舞一場 272 有不舍 有惆悵 可你偏偏把手放 273 你曾說 我是家 轉(zhuǎn)身,又投風(fēng)...
    茉莉的小茶館閱讀 714評論 3 2
  • 在沒遇到他之前,我以為自己是個(gè)獨(dú)立而不黏人的女孩。但磨合期一過,才發(fā)覺自己原來特黏人。原本堅(jiān)強(qiáng)獨(dú)立的外在,都變成了...
    762420323753閱讀 212評論 0 0