apk壓縮包的大小通過優化以後變小html
對apk中的圖片進行壓縮java
【1】svg圖片:文件是對圖片的描述,犧牲CPU的計算能力的,節省空間。linux
【2】webp圖片:在圖片壓縮的時候能夠指定壓縮的格式爲webp。在android中是支持webp圖片顯示的。android
使用的原則:簡單的圖標。git
圖片格式的大小的比較:png>jpeg>webpgithub
webp是由VP8派生而來的。webp的無損壓縮比PNG文件小45%左右,即便PNG進過其餘的壓縮工具壓縮後,能夠減少到PNG的28%。web
使用的廠商:Facebook在用、騰訊、淘寶數組
缺點:加載相比於PNG要慢不少,隨着如今手機配置變高,可使用。服務器
轉換的工具:http://isparta.github.io/微信
常見:emoji表情、皮膚、動態下載資源、模塊的插件化動態加載,例如small等工具。
Lint工具會提供一寫優化建議的點,能夠採用,可是並不是全部的建議都是正確的。
目的也是同樣的,就是爲了減少apk的大小
1)檢測沒有用的佈局 刪除
2)未使用到的資源 好比 圖片 ---刪除
3)建議String.xml有一些沒有用到的字符。
7zZip工具的使用
1)能夠刪除註釋和不用的代碼。
2)將java文件名改爲短名a.java,b.java
3)方法名變爲無心義的字符
資源打包的過程:
【參考】https://www.jianshu.com/p/3cc131db2002,下面的內容摘自連接文章
開發app時,須要代碼和資源。最終生成的apk中代碼轉換爲了dex文件,那麼apk文件中的資源是否仍是app開發時那些資源文件呢?或者說這些資源文件是否發生了什麼變化?
引用老羅一張關於資源打包過程以及查找的圖:
從上圖能夠看出:
除了assets和res/raw資源被原裝不動地打包進APK以外,其它的資源都會被編譯或者處理.xml文件會被編譯爲二進制的xml,因此解壓apk後,沒法直接打開xml文件。
除了assets資源以外,其它的資源都會被賦予一個資源ID。
打包工具負責編譯和打包資源,編譯完成以後,會生成一個resources.arsc文件和一個R.java,前者保存的是一個資源索引表,後者定義了各個資源ID常量,供在代碼中索引資源。
應用程序配置文件AndroidManifest.xml一樣會被編譯成二進制的XML文件,而後再打包到APK裏面去。
應用程序在運行時最終是經過AssetManager來訪問資源,或經過資源ID來訪問,或經過文件名來訪問。
在生成的apk中,只有assets和res/raw資源被原裝不動地打包進apk。其它的資源都會被編譯或者處理。可使用以下命令查看apk中的文件列表:
aapt l -v apkfile
將apk直接解壓後,會發現xml都打不開,提示格式不對,由於其已經變爲二進制xml了。另外PNG等圖片也會進行相應的優化。還有就是多了一個resources.arsc文件。
在android中存在與對應R文件描述的一個二進制的資源文件,名稱爲:resources.arsc
參考資源:https://bbs.pediy.com/thread-207816.htm
Android 中具備18個資源維度,
【原理】再次作混淆:將長名稱改成短名字命名;
實際的替換內容:將資源文件的名稱改成名字,而後將映射文件resources.arsc的中的字符串修改成對應的短名字
再作「混淆」:要實現將res/drawable/ic_launcher.png圖片改爲a.png
drawable文件的名字
String文件的名字
layout的名字
好比:R.string.description--->R.string.a
res/drawable/ic_launcher.png圖片改爲a.png
還能夠更加誇張
res/drawable--->r/d
res/value-->r/v
res/drawable/ic_launcher.png圖片改爲r/d/a.png
依據:根據二進制文件中提供的信息,從新再造一張表(一個二進制文件),區別就是將舊錶的中的字符串信息替換爲簡單的名稱,減少佔用的空間
【LEDataInputStream.java】工具類
1 public final class LEDataInputStream 2 implements DataInput { 3 private static final String EMBEDDED_COPYRIGHT = "copyright (c) 1999-2010 Roedy Green, " + 4 "Canadian Mind Products, http://mindprod.com"; 5 protected final DataInputStream dis; 6 protected final InputStream is; 7 protected final byte[] work; 8 9 public static String readUTF(DataInput in) 10 throws IOException { 11 return DataInputStream.readUTF(in); 12 } 13 14 public LEDataInputStream(InputStream in) { 15 this.is = in; 16 this.dis = new DataInputStream(in); 17 this.work = new byte[8]; 18 } 19 20 public final void close() 21 throws IOException { 22 this.dis.close(); 23 } 24 25 public final int read(byte[] ba, int off, int len) 26 throws IOException { 27 return this.is.read(ba, off, len); 28 } 29 30 public final boolean readBoolean() 31 throws IOException { 32 return this.dis.readBoolean(); 33 } 34 35 public final byte readByte() 36 throws IOException { 37 return this.dis.readByte(); 38 } 39 40 public final char readChar() //讀取一個字節 41 throws IOException { 42 this.dis.readFully(this.work, 0, 2); 43 return (char) ((this.work[1] & 0xFF) << 8 | this.work[0] & 0xFF); 44 } 45 46 public final double readDouble() 47 throws IOException { 48 return Double.longBitsToDouble(readLong()); 49 } 50 51 public final float readFloat() 52 throws IOException { 53 return Float.intBitsToFloat(readInt()); 54 } 55 56 public final void readFully(byte[] ba) 57 throws IOException { 58 this.dis.readFully(ba, 0, ba.length); 59 } 60 61 public final void readFully(byte[] ba, int off, int len) 62 throws IOException { 63 this.dis.readFully(ba, off, len); 64 } 65 66 public final int readInt() //讀4個字節 67 throws IOException { 68 this.dis.readFully(this.work, 0, 4); 69 return (this.work[3] << 24 | (this.work[2] & 0xFF) << 16 | (this.work[1] & 0xFF) << 8 | 70 this.work[0] & 0xFF); 71 } 72 73 @Deprecated 74 public final String readLine() 75 throws IOException { 76 return this.dis.readLine(); 77 } 78 79 public final long readLong() 80 throws IOException { 81 this.dis.readFully(this.work, 0, 8); 82 return (this.work[7] << 56 | 83 (this.work[6] & 0xFF) << 48 | 84 (this.work[5] & 0xFF) << 40 | 85 (this.work[4] & 0xFF) << 32 | 86 (this.work[3] & 0xFF) << 24 | 87 (this.work[2] & 0xFF) << 16 | 88 (this.work[1] & 0xFF) << 8 | 89 this.work[0] & 0xFF); 90 } 91 92 public final short readShort() //讀取2個字節 93 throws IOException { 94 this.dis.readFully(this.work, 0, 2); 95 return (short) ((this.work[1] & 0xFF) << 8 | this.work[0] & 0xFF); 96 } 97 98 public final String readUTF() 99 throws IOException { 100 return this.dis.readUTF(); 101 } 102 103 public final int readUnsignedByte() 104 throws IOException { 105 return this.dis.readUnsignedByte(); 106 } 107 108 public final int readUnsignedShort() 109 throws IOException { 110 this.dis.readFully(this.work, 0, 2); 111 return ((this.work[1] & 0xFF) << 8 | this.work[0] & 0xFF); 112 } 113 114 public final int skipBytes(int n) 115 throws IOException { 116 return this.dis.skipBytes(n); 117 } 118 }
【demo】讀取二進制表resources_arsc的簡單示例
1 /** 2 * @function: 示例解析了resources_arsc二進制表的Resource Table、StringPool、packageHeader 三部份內容,沒有徹底讀取結束 3 * 簡單的演示 4 */ 5 6 public class ArscTest { 7 public static void main(String[] args){ 8 ArscTest arscTest = new ArscTest(); 9 try { 10 // arscTest.readInputStream("C:/Users/luoding/workspace_test/arscTest/input.apk"); 11 // arscTest.readInputStream("C:/Users/luoding/workspace_test/arscTest/Lsn10SearchView.apk"); 12 arscTest.readInputStream("C:/Users/luoding/workspace_test/arscTest/dn_jobschduler.apk"); 13 } catch (IOException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 } 18 19 @SuppressWarnings("resource") 20 public void readInputStream(String path) throws IOException{ 21 //讀取壓縮文件:resources_arsc 22 ZipFile zipFile = new ZipFile(path); 23 //以流形式讀進來 24 InputStream inputStream = zipFile.getInputStream(new ZipEntry(""+"resources.arsc")); 25 LEDataInputStream leDataInputStream = new LEDataInputStream(inputStream); 26 //Resource Table 27 // 讀整個包的RES_TABLE_TYPE,2個字節? 28 short type = leDataInputStream.readShort(); 29 //跳過了--整個包的頭大小:2個字節 30 leDataInputStream.skipBytes(2); 31 //整個包的文件大小 32 leDataInputStream.readInt(); 33 //整個包的package個數,通常的個數是1個 34 int packageNum = leDataInputStream.readInt(); 35 System.out.println("num of package:"+packageNum); 36 37 //StringPool對應的內容 38 // 直接了4個字節:包含RES_STRING_POOL_TYPE+頭的大小,沒有使用?? 39 int got =leDataInputStream.readInt(); 40 //塊大小 41 int chunkSize = leDataInputStream.readInt(); 42 //字符串數量 43 int stringCount = leDataInputStream.readInt(); 44 //style數量 45 int styleCount = leDataInputStream.readInt(); 46 //標記 47 int flags = leDataInputStream.readInt(); 48 //字符串起始位置 49 int stringsOffset = leDataInputStream.readInt(); 50 //style起始位置 51 int stylesOffset = leDataInputStream.readInt(); 52 //下面的循環讀的是字符串的偏移數組,每一個元素存的是字符串的起始位置 53 int[] array = new int[stringCount]; 54 for (int i = 0; i < stringCount; ++i){ 55 array[i] = leDataInputStream.readInt(); 56 } 57 //下面讀取的style的內容 58 if (styleCount != 0) { 59 for (int i = 0; i < styleCount; ++i) 60 array[i] = leDataInputStream.readInt(); 61 } 62 63 //字符串長度 64 int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; 65 byte[] m_strings = new byte[size]; 66 StringBuffer ss = new StringBuffer(); 67 leDataInputStream.readFully(m_strings); 68 //讀取了全部的字符串的內容 69 for(int i = 0;i<m_strings.length;i++){ 70 //(經過打開resources.arsc看到一些亂碼 猜得出字符都是ASCII碼) 71 char c = (char) m_strings[i]; 72 ss.append(c); 73 } 74 System.out.println(ss.toString()); 75 if (stylesOffset != 0) { 76 size = chunkSize - stylesOffset; 77 if (size % 4 != 0) 78 throw new IOException("Style data size is not multiple of 4 (" + size + ")."); 79 80 for (int i = 0; i < size / 4; ++i) 81 leDataInputStream.readInt(); 82 } 83 84 //nextChunk--packageHeader 85 leDataInputStream.readShort(); 86 leDataInputStream.skipBytes(2); 87 leDataInputStream.readInt(); 88 89 int id = (byte) leDataInputStream.readInt(); 90 StringBuilder sb = new StringBuilder(16); 91 int length = 256; 92 //拿包名 93 while (length-- != 0) { 94 short ch = leDataInputStream.readShort(); 95 if (ch == 0) 96 break; 97 sb.append((char)ch); 98 } 99 System.out.println("pacakgeName:"+sb.toString()); 100 } 101 }
1 /** 2 * 程序的總入口: 3 * @param args 4 */ 5 public static void main(String[] args) { 6 mBeginTime = System.currentTimeMillis(); 7 Main m = new Main(); 8 //獲取保存的優化後文件的路徑 9 getRunningLocation(m); 10 //運行程序:須要傳入args參數 11 m.run(args); 12 }
【args參數的來源】
【解析args源碼】是在run方法中:下面截取了部分代碼
1 private void run(String[] args) { 2 if (args.length < 1) { 3 goToError(); 4 } 5 6 File configFile = null;// 配置文件 7 File outputFile = null;// 輸出文件 8 String apkFileName = null;// apk輸入文件 9 10 File signatureFile = null;// 簽名祕鑰 11 File mappingFile = null;// 修改後映射文件 12 String keypass = null;// 祕鑰主密碼 13 String storealias = null;// 別名 14 String storepass = null;// 別名密碼 15 16 String signedFile = null;// 簽名apk 17 // 檢查參數 18 for (int index = 0; index < args.length; ++index) { 19 String arg = args[index]; 20 if ((arg.equals("--help")) || (arg.equals("-h"))) { 21 goToError(); 22 } else if (arg.equals("-config")) {// 配置文件不能最後讀,後面的執行須要依賴配置文件,或者不是.xml則報錯 23 if ((index == args.length - 1) || (!(args[(index + 1)].endsWith(".xml")))) { 24 System.err.println("Missing XML configuration file argument"); 25 goToError(); 26 } 27 // 參數後面的參數纔是config配置文件的路徑 28 configFile = new File(args[(++index)]); 29 if (!(configFile.exists())) { 30 System.err.println(configFile.getAbsolutePath() + " does not exist"); 31 goToError(); 32 } 33 System.out.printf("special configFile file path: %s\n", new Object[] { configFile.getAbsolutePath() });
【config.xml】其中解析的配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <resproguard> 3 <!--defaut property to set --> 4 <issue id="property"> 5 <!--是否使用極限壓縮:7zip --> 6 <!--whether use 7zip to repackage the signed apk, you must install the 7z command line version in window --> 7 <!--sudo apt-get install p7zip-full in linux --> 8 <!--and you must write the sign data fist, and i found that if we use linux, we can get a better result --> 9 <seventzip value="true"/> 10 <!--密鑰--> 11 <!--the sign data file name in your apk, default must be META-INF--> 12 <!--generally, you do not need to change it if you dont change the meta file name in your apk--> 13 <metaname value="META-INF"/> 14 <!--名稱的極端的壓縮--> 15 <!--if keep root, res/drawable will be kept, it won't be changed to such as r/s--> 16 <keeproot value="false"/> 17 </issue>
微信中的r目錄下的文件 命名:
繼續看config.xml的內容
1 <!--whitelist, some resource id you can not proguard, such as getIdentifier--> 2 <!--isactive, whether to use whitelist, you can set false to close it simply--> 3 <!--白名單:能夠指定第三方的資源不被修更名稱 --> 4 <issue id="whitelist" isactive="true"> 5 <!--you must write the full package name, such as com.tencent.mm.R --> 6 <!--for some reason, we should keep our icon better--> 7 <!--and it support *, ?, such as com.tencent.mm.R.drawable.emoji_*, com.tencent.mm.R.drawable.emoji_?--> 8 <path value="com.codeboy.autoresguarddemo.R.mipmap.ic_launcher"/> 9 </issue> 10 11 <!--是否生成映射文件 --> 12 <!--keepmapping, sometimes if we need to support incremental upgrade, we should keep the old mapping--> 13 <!--isactive, whether to use keepmapping, you can set false to close it simply--> 14 <!--if you use -mapping to set sign property in cammand line, these setting will be overlayed--> 15 <issue id="keepmapping" isactive="false"> 16 <!--the old mapping path, in window use \, in linux use /, and the default path is the running location--> 17 <path value="resource_mapping.txt"/> 18 </issue>
映射文件的內容部分截取以下:
config.xml 還包括是否使用簽名,其中指定簽名的路徑和密碼等
1 <!--sign, if you want to sign the apk, and if you want to use 7zip, you must fill in the following data--> 2 <!--isactive, whether to use sign, you can set false to close it simply--> 3 <!--if you use -signature to set sign property in cammand line, these setting will be overlayed--> 4 <issue id="sign" isactive="true"> 5 <!-- <issue id="sign" isactive="false"> --> 6 <!--the signature file path, in window use \, in linux use /, and the default path is the running location--> 7 <!-- <path value="簽名路徑"/> --> 8 <!-- storepass --> 9 <!-- <storepass value="簽名密碼"/> --> 10 <!-- keypass --> 11 <!-- <keypass value="別名密碼"/> --> 12 <!-- alias --> 13 <!-- <alias value="別名"/> --> 14 <path value="C:\Users\luoding\workspace_test\dn_android_resproguard\mykeystore.keystore"/> 15 <!--storepass--> 16 <storepass value="123456"/> 17 <!--keypass--> 18 <keypass value="123456"/> 19 <!--alias--> 20 <alias value="123456"/> 21 </issue>
能夠經過out參數指定輸出的文件的目錄
在生成的打包文件具備多種類型:
其中的zipaligne是對齊,至關於磁盤的碎片整理,具備專門的zipalign工具
參考文章:http://bbs.ihei5.com/thread-171596-1-1.html
關於Android平臺的Zipalign或Zipaligned: 什麼是Zipalign? Zipalign是一個檔案整理工具,它首次被介紹是在Android 1.6版本的SDK(Software Development Kit)軟件開發工具包中。它優化Android應用程序包(APK)到整合包, 以使Android操做系統與應用程序之間的交互做用更有效率,而後應用程序和總體系統的運行速度更快,發揮更大的潛能。它使Zipaligned的應用程序執行時間達到最低限度,其最終結果致使當設備運行APK應用程序時佔更少的RAM(Random Access Memory)隨機訪問內存 Zipalign如何準備的執行(工做)呢? 在Android的操做環境中,存儲在每一個應用程序包的數據文件經過多個進程訪問,例如,程序安裝工具將讀取數據列表肯定相關的權限;由於包括顯示通知等在內的多種緣由,系統服務器能夠讀取這些資源;主界面應用程序要讀取資源以便獲取應用程序的名稱與圖標等。由於Android是基於一個真正的多任務操做基礎架構,這些文件是不斷地讀取。最後但也是最重要的,應用程序自己讀取體現的數據 由於Android操做系統基於Linux架構,存儲單元佈置(Memory Mapping)在有效的處理過程當中起着一個關鍵的做用。從本質上而講,爲Android操做系統資源的處理代碼最佳的整理是4字節界層。這個意思是說,若是APK應用程序包是存儲單元佈置到4字節界層,依據相應的整理,操做系統將不須要通讀整個應用程序包以獲取所須要的數據表,而每個系統處理都將提早知道到哪裏去尋找它所須要的資源,所以執行效率更快(運行更平滑,速度更快) 總結而講,Zipalign一個APK應用程序的結果便是讓全部的未壓縮的數據以整合包的形式被整理到4字節界層,讓全部的數據能夠直接從存儲器映象讀取。而由於正訪問的代碼沒有通讀到整個應用程序包使得執行消耗的程序運行內存(RAM)下降。最終,操做系統總體上的速度會更快 UnZipalign(未整理)的APK應用程序包有什麼劣勢呢? 這是很容易理解的,對於未整理的應用程序包的狀況,資源讀取將會緩慢,程序運行內存(RAM)的使用將會處在一個較高的範圍。它也取決於當前有多少未整理的應用程序。例如,若是有着較少的應用程序,而後有一個未整理的主界面程序,在啓動時間上,你能觀察到更慢的應用程序,這是最理想的狀況。 對於一個糟糕的狀況,若有着許多的未整理的應用程序,將會致使系統反覆的啓動和結束進程,系統運行將會滯後,電池的使用時間將會大幅度下降 咱們應該怎麼作呢? 當前,Zipalign檔案整理工具已變成Android SDK(Software Development Kit)軟件開發工具包的一部分。它能夠在電腦上已安裝好的Android SDK 「tools」 或「platform-tools」 目錄或文件夾中被查看 對於使用Zipalign檔案整理工具,能夠簡單地輸入並執行Command命令—— zipalign [-f] [-v] <alignment> infile.apk outfile.apk 其中,infile.apk是源文件,outfile.apk是輸出文件 更多的,咱們也能夠確認APK應用程序的整理,輸入並執行Command命令—— zipalign -c -v <alignment> existing.apk 其中,existing.apk能被任何你須要的應用程序包得到確認。 另外<alignment>命令須要是一個整數值,否則,命令會返回無效的錯誤提示。而這個值,雖然能夠是任何的整數,但必須始終是4,由於這將提供32位元的整理層,任何其餘的值,它們都是有效的,但不起任何做用 對於這些Command命令的標識—— -f—重寫存在的outfile.zip -v—提供詳細的輸出 -c—確認一個給定的文件的整理 有什麼要注意呢? Zipalign操做必須且僅在標記APK文件附有我的加密鑰以後。若是在標記以前進行Zipalign操做,標記過程將會干擾整理
其中還存在壓縮選項:主要是對圖片的壓縮
1 <!--compress, if you want to compress the file, the name is relative path, such as resources.arsc, res/drawable-hdpi/welcome.png--> 2 <!--what can you compress? generally, if your resources.arsc less than 1m, you can compress it. and i think compress .png, .jpg is ok--> 3 <!--isactive, whether to use compress, you can set false to close it simply--> 4 <issue id="compress" isactive="true"> 5 <!--you must use / separation, and it support *, ?, such as *.png, *.jpg, res/drawable-hdpi/welcome_?.png--> 6 <path value="*.png"/> 7 <path value="*.jpg"/> 8 <path value="*.jpeg"/> 9 <path value="*.gif"/> 10 <path value="resources.arsc"/> 11 </issue>
【完整的配置文件】config.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <resproguard> 3 <!--defaut property to set --> 4 <issue id="property"> 5 <!--是否使用極限壓縮:7zip --> 6 <!--whether use 7zip to repackage the signed apk, you must install the 7z command line version in window --> 7 <!--sudo apt-get install p7zip-full in linux --> 8 <!--and you must write the sign data fist, and i found that if we use linux, we can get a better result --> 9 <seventzip value="true"/> 10 <!--密鑰--> 11 <!--the sign data file name in your apk, default must be META-INF--> 12 <!--generally, you do not need to change it if you dont change the meta file name in your apk--> 13 <metaname value="META-INF"/> 14 <!--名稱的極端的壓縮--> 15 <!--if keep root, res/drawable will be kept, it won't be changed to such as r/s--> 16 <keeproot value="false"/> 17 </issue> 18 19 <!--whitelist, some resource id you can not proguard, such as getIdentifier--> 20 <!--isactive, whether to use whitelist, you can set false to close it simply--> 21 <!--白名單:能夠指定第三方的資源不被修更名稱 --> 22 <issue id="whitelist" isactive="true"> 23 <!--you must write the full package name, such as com.tencent.mm.R --> 24 <!--for some reason, we should keep our icon better--> 25 <!--and it support *, ?, such as com.tencent.mm.R.drawable.emoji_*, com.tencent.mm.R.drawable.emoji_?--> 26 <path value="com.codeboy.autoresguarddemo.R.mipmap.ic_launcher"/> 27 </issue> 28 29 <!--是否生成映射文件 --> 30 <!--keepmapping, sometimes if we need to support incremental upgrade, we should keep the old mapping--> 31 <!--isactive, whether to use keepmapping, you can set false to close it simply--> 32 <!--if you use -mapping to set sign property in cammand line, these setting will be overlayed--> 33 <issue id="keepmapping" isactive="false"> 34 <!--the old mapping path, in window use \, in linux use /, and the default path is the running location--> 35 <path value="resource_mapping.txt"/> 36 </issue> 37 38 <!--compress, if you want to compress the file, the name is relative path, such as resources.arsc, res/drawable-hdpi/welcome.png--> 39 <!--what can you compress? generally, if your resources.arsc less than 1m, you can compress it. and i think compress .png, .jpg is ok--> 40 <!--isactive, whether to use compress, you can set false to close it simply--> 41 <issue id="compress" isactive="true"> 42 <!--you must use / separation, and it support *, ?, such as *.png, *.jpg, res/drawable-hdpi/welcome_?.png--> 43 <path value="*.png"/> 44 <path value="*.jpg"/> 45 <path value="*.jpeg"/> 46 <path value="*.gif"/> 47 <path value="resources.arsc"/> 48 </issue> 49 50 <!--sign, if you want to sign the apk, and if you want to use 7zip, you must fill in the following data--> 51 <!--isactive, whether to use sign, you can set false to close it simply--> 52 <!--if you use -signature to set sign property in cammand line, these setting will be overlayed--> 53 <issue id="sign" isactive="true"> 54 <!-- <issue id="sign" isactive="false"> --> 55 <!--the signature file path, in window use \, in linux use /, and the default path is the running location--> 56 <!-- <path value="簽名路徑"/> --> 57 <!-- storepass --> 58 <!-- <storepass value="簽名密碼"/> --> 59 <!-- keypass --> 60 <!-- <keypass value="別名密碼"/> --> 61 <!-- alias --> 62 <!-- <alias value="別名"/> --> 63 <path value="C:\Users\luoding\workspace_test\dn_android_resproguard\mykeystore.keystore"/> 64 <!--storepass--> 65 <storepass value="123456"/> 66 <!--keypass--> 67 <keypass value="123456"/> 68 <!--alias--> 69 <alias value="123456"/> 70 </issue> 71 72 </resproguard>
源碼下載地址:https://download.csdn.net/download/wsxingjun/10584278