Ëæ×ÅÒµÎñµÄ¿ìËÙµü´úÔö³¤£¬ÃÀÍÅAppÀï²»¶ÏÒýÈëеÄÒµÎñÂß¼´úÂ롢ͼƬ×ÊÔ´ºÍµÚÈý·½SDK£¬Ö±½Óµ¼ÖÂAPKÌå»ý²»¶ÏÔö³¤¡£°üÌå»ýÔö³¤´øÀ´µÄÎÊÌâÔ½À´Ô½¶à£¬ÈçCDNÁ÷Á¿·ÑÓÃÔö¼Ó¡¢Óû§°²×°³É¹¦ÂʽµµÍ£¬ÉõÖÁ¿ÉÄÜ»áÓ°ÏìÓû§µÄÁô´æÂÊ¡£APKµÄÊÝÉíÒѾÊDz»µÃ²»¿¼ÂǵÄÊÂÇé¡£ÔÚ³¢ÊÔÊÝÉíµÄ¹ý³ÌÖУ¬ÎÒÃÇ½è¼øÁ˺ܶàÒµ½çÆäËû¹«Ë¾ÌṩµÄ·½°¸£¬Í¬Ê±Ò²Õë¶Ô×ÔÉíÌØµã£¬·¢ÏÖÁËһЩеļ¼ÇÉ¡£±¾ÎĽ«¶ÔÆäÖеÄһЩ×öÏêϸ½éÉÜ¡£
ÔÚ¿ªÊ¼½²ÊÝÉí¼¼ÇÉ֮ǰ£¬ÏÈÀ´½²Ò»ÏÂAPKµÄ¹¹³É¡£
APKµÄ¹¹³É
¿ÉÒÔÓÃZip¹¤¾ß´ò¿ªAPK²é¿´¡£±ÈÈ磬ÃÀÍÅApp 7.8.6µÄÏßÉϰ汾µÄ¸ñʽÊÇÕâÑùµÄ£º

¿ÉÒÔ¿´µ½APKÓÉÒÔÏÂÖ÷Òª²¿·Ö×é³É£º

µ±È»»¹»áÓÐһЩÆäËüµÄÎļþ£¬ÀýÈçÉÏͼÖеÄorg/¡¢src/¡¢push_versionµÈÎļþ»òÎļþ¼Ð¡£ÕâЩ×ÊÔ´ÊÇJava
Resources£¬¸ÐÐËȤµÄ¿ÉÒÔ½áºÏ±àÒ빤×÷Á÷ÖеÄÁ÷³ÌͼÒÔ¼° MergeJavaResourcesTransform
µÄÔ´Âë¿´¿´±»´òÈëAPK°üÖеÄ×ÊÔ´¶¼ÓÐÄÄЩ£¬ÕâÀï²»×ö¹ý¶à½éÉÜ¡£
ÔÚ³ä·ÖÁ˽âÁËAPK¸÷¸ö×é³É²¿·ÖÒÔ¼°ËüÃǵÄ×÷Óúó£¬ÎÒÃÇÕë¶Ô×ÔÉíÌØµã½øÐÐÁË·ÖÎöºÍÓÅ»¯¡£ÏÂÃæ½«´ÓZipÎļþ¸ñʽ¡¢classes.dex¡¢×ÊÔ´Îļþ¡¢resources.arscµÈ·½ÃæÀ´½éÉÜÏÂÎÒÃÇ·¢ÏֵIJ¿·ÖÓÅ»¯¼¼ÇÉ¡£
Zip¸ñʽÓÅ»¯
Ç°Ãæ½éÉÜÁËAPKµÄÎļþ¸ñʽÒÔ¼°Ö÷Òª×é³É²¿·Ö£¬Í¨¹ýaapt l -v xxx.apk»òunzip -l
xxx.apkÀ´²é¿´APKÎļþʱ»áµÃµ½ÒÔÏÂÐÅÏ¢£¬¼ûÏÂÃæ½ØÍ¼£º

ͨ¹ýÉÏͼ¿ÉÒÔ¿´µ½APKÖкܶà×ÊÔ´ÊÇÒÔStoredÀ´´æ´¢µÄ£¬¸ù¾ÝZipµÄÎļþ¸ñʽÖжÔѹËõ·½Ê½µÄÃèÊö
Compression_methods ¿ÉÒÔ¿´³öÕâЩÎļþÊÇûÓÐѹËõµÄ£¬ÄÇΪʲôËüÃÇûÓб»Ñ¹ËõÄØ£¿´ÓAAPTµÄÔ´ÂëÖÐÕÒµ½ÒÔÏÂÃèÊö£º
/* these formats
are already compressed, or don't compress well
*/
static const char* kNoCompressExt[] = { ".jpg",
".jpeg", ".png", ".gif",
".wav", ".mp2", ".mp3",
".ogg", ".aac", ".mpg",
".mpeg", ".mid", ".midi",
".smf", ".jet", ".rtttl",
".imy", ".xmf", ".mp4",
".m4a", ".m4v", ".3gp",
".3gpp", ".3g2", ".3gpp2",
".amr", ".awb", ".wma",
".wmv", ".webm", ".mkv"
}; |
¿ÉÒÔ¿´³öAAPTÔÚ×ÊÔ´´¦Àíʱ¶ÔÕâЩÎļþºó׺ÀàÐ͵Ä×ÊÔ´ÊDz»×öѹËõµÄ£¬ÄÇÊDz»ÊÇ¿ÉÒÔÐÞ¸ÄËüÃǵÄѹËõ·½Ê½´Ó¶ø´ïµ½ÊÝÉíµÄЧ¹ûÄØ£¿
ÔÚ½éÉÜÔõô×ö֮ǰ£¬ÏÈÀ´´ó¸Å½éÉÜÒ»ÏÂAppµÄ×ÊÔ´ÊÇÔõô±»´ò½øAPK°üÀïµÄ¡£Android¹¹½¨¹¤¾ßÁ´Ê¹ÓÃAAPT¹¤¾ßÀ´¶Ô×ÊÔ´½øÐд¦Àí£¬À´¿´ÏÂͼ£¨Í¼Æ¬À´Ô´ÓÚBuild
Workflow£©£º

ͨ¹ýÉÏͼ¿ÉÒÔ¿´µ½Manifest¡¢ Resources¡¢AssetsµÄ×ÊÔ´¾¹ýAAPT´¦ÀíºóÉú³ÉR.java¡¢
Proguard Configuration ¡¢Compiled Resources¡£ÆäÖÐR.java´ó¼Ò¶¼±È½ÏÊìϤ£¬ÕâÀï¾Í²»¹ý¶à½éÉÜÁË¡£ÎÒÃÇÀ´Öص㿴¿´Proguard
Configuration ¡¢Compiled Resources¶¼ÊÇ×öʲôµÄÄØ£¿
Proguard Configuration ÊÇAAPT¹¤¾ßΪManifestÖÐÉùÃ÷µÄËÄ´ó×é¼þÒÔ¼°²¼¾ÖÎļþÖУ¨XML
layouts£©Ê¹Óõĸ÷ÖÖViewsËùÉú³ÉµÄProGuardÅäÖ㬸ÃÎļþͨ³£´æ·ÅÔÚ${project.buildDir}/
$ {AndroidProject.FD _INTERMEDIATES} /proguard-rules/
${flavorName}/ ${buildType}/aapt_rules.txt£¬ÏÂÃæÊÇÏîÄ¿ÖиÃÎļþµÄ½ØÍ¼£¬ºì¿ò±ê¼Ç³öÀ´µÄ¾ÍÊǶÔAndroidManifest.xml
¡¢XML LayoutsÖÐÏà¹Ø ClassµÄProGuardÅäÖá£

Compiled ResourcesÊÇÒ»¸öZip¸ñʽµÄÎļþ£¬Õâ¸öÎļþµÄ·¾¶Í¨³£Îª
${project.buildDir} /$ {AndroidProject.FD _INTERMEDIATES}
/res/resources-$ {flavorName } -${buildType} -stripped.ap_¡£
ͨ¹ýÏÂÃæ¾¹ýZip½âѹºóµÄ½ØÍ¼£¬¿ÉÒÔ¿´³öÕâ¸öÎļþ°üº¬ÁËres¡¢ AndroidManifest.xml ºÍresources.arscµÄÎļþ»òÎļþ¼Ð¡£½áºÏBuild
WorkflowÖеÄÃèÊö£¬¿ÉÒÔ¿´³öÕâ¸öÎļþ£¨resources-$ {flavorName} -$
{buildType} -stripped.ap_£©»á±»apkbuilder´ò°üµ½APK°üÖУ¬ËüÆäʵ¾ÍÊÇAPKµÄ¡°×ÊÔ´°ü¡±£¨res¡¢ AndroidManifest.xml ºÍresources.arsc£©¡£

ÎÒÃǾÍÊÇͨ¹ýÕâ¸öÎļþÀ´Ð޸IJ»Í¬ºó׺Îļþ×ÊÔ´µÄѹËõ·½Ê½À´´ïµ½ÊÝÉíЧ¹ûµÄ£¬¶øÔÚºóÃæ¡°resources.arscµÄÓÅ»¯¡±Ò»½ÚÖÐÒ²ÊDzÙ×÷µÄÕâ¸öÎļþ¡£
±ÊÕßÔÚ×Ô¼ºµÄÏîÄ¿ÖÐÊÇͨ¹ýÔÚpackage${flavorName} Task£¨¸ÐÐËȤµÄͬѧ¿ÉÒԲ鿴ԴÂ룩֮ǰ½øÐÐÕâ¸ö²Ù×÷µÄ¡£
ÏÂÃæÊDz¿·Ö´úÂëÆ¬¶Î£º
appPlugin.variantManage
r.variantDataList.each { variantData ->
variantData.outputs.each {
def sourceApFile = it.packageAndroidArtifactTask
.getResourceFile();
def destApFile = new File ("${sourceApFile.name}.temp",
sourceApFile.parentFile);
it.packageAndroidArtifactTask.doFirst {
byte[] buf = new byte[1024 * 8];
ZipInputStream zin = new ZipInputStream (new
FileInputStream(sourceApFile));
ZipOutputStream out = new ZipOutputStream (new
FileOutputStream(destApFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String name = entry.getName();
// Add ZIP entry to output stream.
ZipEntry zipEntry = new ZipEntry(name);
if (ZipEntry.STORED == entry.getMethod() &&
!okayToCompress (entry.getName())) {
zipEntry.setMethod(ZipEntry.STORED)
zipEntry.setSize(entry.getSize())
zipEntry.setCompressedSize (entry.getCompressedSize())
zipEntry.setCrc(entry.getCrc())
} else {
zipEntry.setMethod(ZipEntry.DEFLATED)
...
}
...
out.putNextEntry(zipEntry);
out.closeEntry();
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
out.close();
sourceApFile.delete();
destApFile.renameTo(sourceApFile);
}
}
} |
µ±È»Ò²¿ÉÒÔÔÚÆäËü¹¹½¨²½ÖèÖвÉÓøü¸ßѹËõÂʵķ½Ê½À´´ïµ½ÊÝÉíЧ¹û£¬ÀýÈç²ÉÓÃ7ZipѹËõµÈµÈ¡£
±¾¼¼ÇɵÄʹÓÃÐèҪעÒâÒÔÏÂÎÊÌ⣺
Èç¹ûÒôÊÓÆµ×ÊÔ´±»Ñ¹Ëõ´æ·ÅÔÚAPKÖеϰ£¬ÔÚʹÓÃһЩÒôƵ¡¢ÊÓÆµAPIʱÓÈÆäҪעÒ⣬ÐèÒª×öºÃ³ä·ÖµÄ²âÊÔ¡£
resources.arscÎļþ×îºÃ²»ÒªÑ¹Ëõ´æ´¢£¬Èç¹ûѹËõ»áÓ°ÏìÒ»¶¨µÄÐÔÄÜ£¨ÓÈÆäÊÇÀäÆô¶¯Ê±¼ä£©¡£
Èç¹ûÏëÔÚAndroid 6.0ÉÏ¿ªÆô android:extractNativeLibs =¡±false
¡±µÄ»°£¬.so ÎļþÒ²²»Äܱ»Ñ¹Ëõ£¬ android:extractNativeLibsµÄʹÓÃ×ËÊÆ¿´ÕâÀApp
Manifest --- application¡£
classes.dexµÄÓÅ»¯
ÈçºÎÓÅ»¯classes.dexµÄ´óÐ¡ÄØ£¿´óÌåÓÐÈçÏÂÌ×·£º
ʱ¿Ì±£³ÖÁ¼ºÃµÄ±à³Ìϰ¹ßºÍ¶Ô°üÌå»ýÃôÈñµÄÐá¾õ£¬È¥³ýÖØ¸´»òÕß²»ÓõĴúÂ룬É÷ÓõÚÈý·½¿â£¬Ñ¡ÓÃÌå»ýСµÄµÚÈý·½SDKµÈµÈ¡£
¿ªÆôProGuardÀ´½øÐдúÂëѹËõ£¬Í¨¹ýʹÓÃProGuardÀ´¶Ô´úÂë½øÐлìÏý¡¢ÓÅ»¯¡¢Ñ¹ËõµÈ¹¤×÷¡£
Õë¶ÔµÚÒ»ÖÖÌ×·£¬Òò¸÷¸ö¹«Ë¾µÄÏîÄ¿µÄ²îÒ죬¹²ÐԵĶ«Î÷½ÏÉÙ£¬ÐèÒªcase by caseµÄ·ÖÎö£¬ÕâÀï²»×ö¹ý¶àµÄ½éÉÜ¡£
ѹËõ´úÂë
¿ÉÒÔͨ¹ý¿ªÆôProGuardÀ´ÊµÏÖ´úÂëѹËõ£¬¿ÉÒÔÔÚbuild.gradleÎļþÏàÓ¦µÄ¹¹½¨ÀàÐÍÖÐÌí¼ÓminifyEnabled
true¡£
Çë×¢Ò⣬´úÂëѹËõ»áÍÏÂý¹¹½¨ËÙ¶È£¬Òò´ËÓ¦¸Ã¾¡¿ÉÄܱÜÃâÔÚµ÷ÊÔ¹¹½¨ÖÐʹÓᣲ»¹ýÒ»¶¨ÒªÎªÓÃÓÚ²âÊÔµÄ×îÖÕAPKÆôÓôúÂëѹËõ£¬Èç¹û²»Äܳä·ÖµØ×Ô¶¨ÒåÒª±£ÁôµÄ´úÂ룬¿ÉÄÜ»áÒýÈë´íÎó¡£
ÀýÈ磬ÏÂÃæÕâ¶ÎÀ´×Ôbuild.gradleÎļþµÄ´úÂëÓÃÓÚΪ·¢²¼¹¹½¨ÆôÓôúÂëѹËõ£º
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(¡®proguard-android.txt'),
'proguard-rules.pro'
}
}
...
} |
³ýÁËminifyEnabledÊôÐÔÍ⣬»¹ÓÐÓÃÓÚ¶¨ÒåProGuard¹æÔòµÄproguardFilesÊôÐÔ£º
getDefaultProguardFile (¡®proguard-android.txt')
ÊÇ´Ó Android SDKtools/proguard/ Îļþ¼Ð»ñȡĬÈÏProGuardÉèÖá£
proguard-rules.proÎļþÓÃÓÚÌí¼Ó×Ô¶¨ÒåProGuard¹æÔò¡£Ä¬ÈÏÇé¿öÏ£¬¸ÃÎļþλÓÚÄ£¿é¸ùĿ¼£¨build.gradleÎļþÅÔ£©¡£
Ìáʾ£ºÒªÏë×ö½øÒ»²½µÄ´úÂëѹËõ£¬¿É³¢ÊÔʹÓÃλÓÚͬһλÖÃµÄ proguard-android-optimize.txt
Îļþ¡£Ëü°üÀ¨ÏàͬµÄProGuard¹æÔò£¬µ«»¹°üÀ¨ÆäËûÔÚ×Ö½ÚÂëÒ»¼¶£¨·½·¨Äںͷ½·¨¼ä£©Ö´ÐзÖÎöµÄÓÅ»¯£¬ÒÔ½øÒ»²½¼õСAPK´óСºÍ°ïÖúÌá¸ßÆäÔËÐÐËÙ¶È¡£
ÔÚGradle Plugin 2.2.0¼°ÒÔÉϰ汾ProGuardµÄÅäÖÃÎļþ»á×Ô¶¯½âѹËõµ½
$ {rootProject.buildDir} /${AndroidProject .FD_INTERMEDIATES}
/proguard-files/Ŀ¼Ï£¬proguardFiles»á´ÓÕâ¸öĿ¼À´»ñÈ¡ProGuardÅäÖá£
ÿ´ÎÖ´ÐÐÍêProGuardÖ®ºó£¬ProGuard¶¼»áÔÚ ${project.buildDir}
/ outputs/mapping/ $ {flavorDir}/Éú³ÉÒÔÏÂÎļþ£º

¿ÉÒÔͨ¹ýÔÚusage.txtÎļþÖп´µ½ÄÄЩ´úÂ뱻ɾ³ýÁË£¬ÈçÏÂͼÖÐËùʾandroid .support.multidex .MultiDexÒѾ±»É¾³ýÁË£º

R FieldµÄÓÅ»¯
³ýÁ˶ÔÏîÄ¿´úÂëÓÅ»¯ºÍ¿ªÆô´úÂëѹËõÖ®Í⣬±ÊÕßÔÚ¡¶ÃÀÍÅAndroid DEX×Ô¶¯²ð°ü¼°¶¯Ì¬¼ÓÔØ¼ò½é¡·ÕâÆªÎÄÕÂÖÐÌáµ½ÁËͨ¹ýÄÚÁªR
FieldÀ´½â¾öR Field¹ý¶àµ¼ÖÂMultiDex 65536µÄÎÊÌ⣬¶øÕâÒ»²½Öè¶Ô´úÂëÊÝÉíÄܹ»Æðµ½Ã÷ÏÔµÄЧ¹û¡£ÏÂÃæÊDZÊÕßͨ¹ý×Ö½ÚÂ빤¾ßÔÚ¹¹½¨Á÷³ÌÖÐÄÚÁªR
FieldµÄ´úÂëÆ¬¶Î£¨×Ö½ÚÂëµÄÐ޸ĿÉÒÔʹÓÃJavassist»òÕßASM£¬¸Ã²½Öè±ÊÕß²ÉÓõÄÊÇJavassist£©¡£
ctBehaviors.each
{ CtBehavior ctBehavior ->
if (!ctBehavior.isEmpty()) {
try {
ctBehavior.instrument(new ExprEditor() {
@Override
public void edit(FieldAccess f) {
try {
def fieldClassName = JavassistUtils .getClassNameFromCtClass (f.getCtClass())
if (shouldInlineRField (className, fieldClassName)
&& f.isReader()) {
def temp = fieldClassName.substring (fieldClassName.indexOf (ANDROID_RESOURCE_R_FLAG)
+ ANDROID_RESOURCE_R_FLAG .length())
def fieldName = f.fieldName
def key = & quot;${temp} .${fieldName}"
if (resourceSymbols .containsKey (key)) {
Object obj = resourceSymbols.get(key)
try {
if (obj instanceof Integer) {
int value = ((Integer) obj).intValue()
f.replace("\$_=${value};")
} else if (obj instanceof Integer[]) {
def obj2 = ((Integer[]) obj)
StringBuilder stringBuilder = new StringBuilder()
for (int index = 0; index < obj2.length;
++index) {
stringBuilder.append (obj2[index].intValue())
if (index != obj2.length - 1) {
stringBuilder.append(",")
}
}
f.replace("\$_ = new int[] {${stringBuilder.toString()}};")
} else {
throw new GradleException (" Unknown ResourceSymbols
Type!")
}
} catch (NotFoundException e) {
throw new GradleException(e.message)
} catch (CannotCompileException e) {
throw new GradleException(e.message)
}
} else {
throw new GradleException ("******** InlineRFieldTask
unprocessed ${className}, ${fieldClassName},
${f.fieldName}, ${key}")
}
}
} catch (NotFoundException e) {
}
}
})
} catch (CannotCompileException e) {
}
}
} |
ÆäËüÓÅ»¯ÊÖ¶Î
Õë¶Ô´úÂëµÄÊÝÉí»¹ÓкܶàÓÅ»¯µÄ¼¼ÇÉ£¬ÀýÈ磺
¼õÉÙENUMµÄʹÓã¨ÏêÇé¿ÉÒԲο¼£ºRemove Enumerations£©£¬Ã¿¼õÉÙÒ»¸öENUM¿ÉÒÔ¼õÉÙ´óÔ¼1.0µ½1.4
KBµÄ´óС£»
ͨ¹ýpmd cpdÀ´¼ì²éÖØ¸´µÄ´úÂë´Ó¶ø½øÐдúÂëÓÅ»¯£»
ÒÆ³ýµôËùÓÐÎÞÓûòÕß¹¦ÄÜÖØ¸´µÄÒÀÀµ¿â¡£
ÕâЩÓÅ»¯¼¼ÇɾͲ»Õ¹¿ª½éÉÜÁË¡£
×ÊÔ´µÄÓÅ»¯
ͼƬÓÅ»¯
ΪÁËÖ§³ÖAndroidÉ豸DPIµÄ¶àÑù»¯ £¨[l|m|tv|h|x|xx|xxx]dpi£©ÒÔ¼°Óû§¶Ô¸ßÖÊÁ¿UIµÄÆÚ´ý£¬ÃÀÍÅAppÖÐʹÓÃÁË´óÁ¿µÄͼƬ£¬ÔÚAndroidÏÂÖ§³ÖºÜ¶à¸ñʽµÄͼƬ£¬ÀýÈ磺PNG¡¢JPG
¡¢WebP£¬ÄÇÎÒÃǸÃÔõôѡÔñ²»Í¬ÀàÐ͵ÄͼƬ¸ñÊ½ÄØ£¿ ÔÚGoogle I/O 2016ÖÐÌáµ½ÁËÕë¶ÔͼƬ¸ñʽµÄÑ¡Ôñ£¬À´¿´ÏÂͼ£¨Í¼Æ¬À´Ô´ÓÚImage
compression for Android developers£©£º

ͨ¹ýÉÏͼ¿ÉÒÔ¿´³öÒ»¸ö´ó¸ÅͼƬ¸ñʽѡÔñµÄ·½·¨¡£Èç¹ûÄÜÓÃVectorDrawableÀ´±íʾµÄ»°ÓÅÏÈʹÓÃVectorDrawable£¬Èç¹ûÖ§³ÖWebPÔòÓÅÏÈÓÃWebP£¬¶øPNGÖ÷ÒªÓÃÔÚչʾ͸Ã÷»òÕß¼òµ¥µÄͼƬ£¬¶øÆäËü³¡¾°¿ÉÒÔʹÓÃJPG¸ñʽ¡£Õë¶ÔÿÖÖͼƬ¸ñʽҲÓи÷ÀàµÄÓÅ»¯ÊֶκÍÓÅ»¯¹¤¾ß¡£
ʹÓÃʸÁ¿Í¼Æ¬
¿ÉÒÔʹÓÃʸÁ¿Í¼ÐÎÀ´´´½¨¶ÀÁ¢ÓÚ·Ö±æÂʵÄͼ±êºÍÆäËû¿ÉÉìËõͼƬ¡£Ê¹ÓÃʸÁ¿Í¼Æ¬Äܹ»ÓÐЧµÄ¼õÉÙAppÖÐͼƬËùÕ¼ÓõĴóС£¬Ê¸Á¿Í¼ÐÎÔÚAndroidÖбíʾΪVectorDrawable¶ÔÏó¡£
ʹÓÃVectorDrawable¶ÔÏó£¬100×Ö½ÚµÄÎļþ¿ÉÒÔÉú³ÉÆÁÄ»´óСµÄÇåÎúͼÏñ£¬µ«ÏµÍ³äÖȾÿ¸öVectorDrawable¶ÔÏóÐèÒª´óÁ¿µÄʱ¼ä£¬½Ï´óµÄͼÏñÐèÒª¸ü³¤µÄʱ¼ä²ÅÄܳöÏÖÔÚÆÁÄ»ÉÏ¡£
Òò´ËÖ»ÓÐÔÚÏÔʾСͼÏñʱ²Å¿¼ÂÇʹÓÃʸÁ¿Í¼ÐΡ£ÓйØÊ¹ÓÃVectorDrawableµÄ¸ü¶àÐÅÏ¢£¬Çë²ÎÔÄ Working
with Drawables¡£
ʹÓÃWebP
Èç¹ûAppµÄminSdkVersion¸ßÓÚ14£¨Android 4.0+£©µÄ»°£¬¿ÉÒÔÑ¡ÓÃWebP¸ñʽ£¬ÒòΪWebPÔÚͬ»ÖÊÏÂÌå»ý¸üС£¨WebPÖ§³Ö͸Ã÷¶È£¬Ñ¹Ëõ±È±ÈJPEG¸ü¸ßµ«ÏÔʾЧ¹ûÈ´²»ÊäÓÚJPEG£¬¹Ù·½ÆÀ²âquality²ÎÊýµÈÓÚ75¾ùºâ×î¼Ñ£©£¬
¿ÉÒÔͨ¹ýPNGµ½WebPת»»¹¤¾ßÀ´½øÐÐת»»¡£µ±È»Android´Ó4.0²Å¿ªÊ¼WebPµÄÔÉúÖ§³Ö£¬µ«ÊDz»Ö§³Ö°üº¬Í¸Ã÷¶È£¬Ö±µ½Android
4.2.1+²ÅÖ§³ÖÏÔʾº¬Í¸Ã÷¶ÈµÄWebP£¬ÔÚ±ÊÕßʹÓÃÖÐÊÇÅжϵ±Ç°AppµÄminSdkVersionÒÔ¼°Í¼Æ¬ÎļþµÄÀàÐÍ£¨ÊÇ·ñΪ͸Ã÷£©À´Ñ¡ÓÃÊÇ·ñÊÊÓÃWebP¡£¼ûÏÂÃæµÄ´úÂëÆ¬¶Î£º
boolean isPNGWebpConvertSupported()
{
if (!isWebpConvertEnable()) {
return false
}
// Android 4.0+
return GradleUtils .getAndroidExtension(project) .defaultConfig.minSdkVersion .apiLevel
>= 14
// 4.0
}
boolean is TransparencyPNGWebpConvertSupported
() {
if (!isWebpConvertEnable()) {
return false
}
// Lossless, Transparency, Android 4.2.1+
return GradleUtils .getAndroidExtension (project) .defaultConfig .minSdkVersion .apiLevel
>= 18
// 4.3
}
def convert() {
String resPath = "${project.buildDir} /$ {AndroidProject .FD_INTERMEDIATES} /res/merged/$ {variant.dirName}"
def resDir = new File("${resPath}")
resDir .eachDirMatch(~/drawable[a-z0-9-]*/) {
dir ->
FileTree tree = project.fileTree(dir: dir)
tree.filter { File file ->
return (isJPGWebpConvertSupported () &&
(file.name .endsWith (SdkConstants.DOT_JPG) ||
file.name.endsWith (SdkConstants.DOT_JPEG)))
|| (isPNGWebpConvertSupported () && file.name.endsWith (SdkConstants.DOT_PNG)
&& !file .name.endsWith (SdkConstants.DOT_9PNG))
}.each { File file ->
def shouldConvert = true
if (file.name.endsWith (SdkConstants.DOT_PNG))
{
if (!isTransparencyPNGWebp ConvertSupported ())
{
shouldConvert = !Imaging.getImageInfo(file) .isTransparent()
}
}
if (shouldConvert) {
WebpUtils.encode (project, webpFactorQuality,
file.absolutePath, webp)
}
}
}
} |
Ñ¡Ôñ¸üÓŵÄѹËõ¹¤¾ß
¿ÉÒÔʹÓÃpngcrush¡¢pngquant»òzopflipngµÈѹËõ¹¤¾ßÀ´¼õÉÙPNGÎļþ´óС£¬¶ø²»»á¶ªÊ§Í¼ÏñÖÊÁ¿¡£ËùÓÐÕâЩ¹¤¾ß¶¼¿ÉÒÔ¼õÉÙPNGÎļþ´óС£¬Í¬Ê±±£³ÖͼÏñÖÊÁ¿¡£
pngcrush¹¤¾ßÌØ±ðÓÐЧ£º´Ë¹¤¾ßÔÚPNG¹ýÂËÆ÷ºÍzlib£¨Deflate£©²ÎÊýÉϵü´ú£¬Ê¹ÓùýÂËÆ÷ºÍ²ÎÊýµÄÿ¸ö×éºÏÀ´Ñ¹ËõͼÏñ¡£È»ºóÑ¡Ôñ²úÉú×îСѹËõÊä³öµÄÅäÖá£
¶ÔÓÚJPEGÎļþ£¬Äã¿ÉÒÔʹÓÃpackJPG»òguetzliµÈ¹¤¾ß½«JPEGÎļþѹËõµÄ¸üС£¬ÕâЩ¹¤¾ßÄܹ»ÔÚ±£³ÖͼƬÖÊÁ¿²»±äµÄÇé¿öÏ£¬°ÑͼƬÎļþѹËõµÄ¸üС¡£guetzli¹¤¾ß¸üÊÇÄܹ»ÔÚͼƬÖÊÁ¿²»±äµÄÇé¿öÏ£¬½«Îļþ´óС½µµÍ35%¡£
ÔÚAndroid¹¹½¨Á÷³ÌÖÐAAPT»áʹÓÃÄÚÖõÄѹËõËã·¨À´ÓÅ»¯res/drawable/Ŀ¼ÏµÄPNGͼƬ£¬µ«Ò²¿ÉÄܻᵼÖ±¾À´ÒѾÓÅ»¯¹ýµÄͼƬÌå»ý±ä´ó£¬¿ÉÒÔͨ¹ýÔÚbuild.gradleÖÐÉèÖÃcruncherEnabledÀ´½ûÖ¹AAPTÀ´ÓÅ»¯PNGͼƬ¡£
aaptOptions {
cruncherEnabled = false
} |
¿ªÆô×ÊԴѹËõ
AndroidµÄ±àÒ빤¾ßÁ´ÖÐÌṩÁËÒ»¿î×ÊԴѹËõµÄ¹¤¾ß£¬¿ÉÒÔͨ¹ý¸Ã¹¤¾ßÀ´Ñ¹Ëõ×ÊÔ´£¬Èç¹ûÒªÆôÓÃ×ÊԴѹËõ£¬¿ÉÒÔÔÚbuild.gradleÎļþÖн«shrinkResources
true¡£ÀýÈ磺
android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
} |
ÐèҪעÒâµÄÊÇĿǰ×ÊԴѹËõÆ÷Ŀǰ²»»áÒÆ³ývalues/Îļþ¼ÐÖж¨ÒåµÄ×ÊÔ´£¨ÀýÈç×Ö·û´®¡¢³ß´ç¡¢ÑùʽºÍÑÕÉ«£©£¬ÓйØÏêÇ飬Çë²ÎÔÄÎÊÌâ
70869¡£
Android¹¹½¨¹¤¾ßÊÇͨ¹ýResourceUsageAnalyzerÀ´¼ì²éÄÄЩ×ÊÔ´ÊÇÎÞÓõ쬵±¼ì²éµ½ÎÞÓõÄ×ÊԴʱ»á°Ñ¸Ã×ÊÔ´Ìæ»»³ÉÔ¤¶¨ÒåµÄ°æ±¾¡£Ïê¿´ÏÂÃæ´úÂëÆ¬¶Î£¨Õª×Ôcom.android .build .gradle.tasks .ResourceUsageAnalyzer£©£º
public class
ResourceUsageAnalyzer {
...
/**
* Whether we should create small/empty dummy
files instead of actually
* removing file resources. This is to work around
crashes on some devices
* where the device is traversing resources.
See http://b.android.com/79325 for more.
*/
public static final boolean REPLACE_DELETED_WITH_EMPTY
= true;
// A 1x1 pixel PNG of type BufferedImage.TYPE_BYTE_GRAY
public static final byte[] TINY_PNG = new byte[]
{
(byte)-119, (byte) 80, (byte) 78, (byte) 71,
(byte) 13, (byte) 10,
(byte) 26, (byte) 10, (byte) 0, (byte) 0, (byte)
0, (byte) 13,
(byte) 73, (byte) 72, (byte) 68, (byte) 82,
(byte) 0, (byte) 0,
(byte) 0, (byte) 1, (byte) 0, (byte) 0, (byte)
0, (byte) 1,
(byte) 8, (byte) 0, (byte) 0, (byte) 0, (byte)
0, (byte) 58,
(byte) 126, (byte)-101, (byte) 85, (byte) 0,
(byte) 0, (byte) 0,
(byte) 10, (byte) 73, (byte) 68, (byte) 65,
(byte) 84, (byte) 120,
(byte) -38, (byte) 99, (byte) 96, (byte) 0,
(byte) 0, (byte) 0,
(byte) 2, (byte) 0, (byte) 1, (byte) -27, (byte)
39, (byte) -34,
(byte) -4, (byte) 0, (byte) 0, (byte) 0, (byte)
0, (byte) 73,
(byte) 69, (byte) 78, (byte) 68, (byte) -82,
(byte) 66, (byte) 96,
(byte)-126
};
public static final long TINY_PNG_CRC = 0x88b2a3b0L;
// A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB
with 9-patch markers
public static final byte[] TINY_9PNG = new byte[]
{
(byte)-119, (byte) 80, (byte) 78, (byte) 71,
(byte) 13, (byte) 10,
(byte) 26, (byte) 10, (byte) 0, (byte) 0, (byte)
0, (byte) 13,
(byte) 73, (byte) 72, (byte) 68, (byte) 82,
(byte) 0, (byte) 0,
(byte) 0, (byte) 3, (byte) 0, (byte) 0, (byte)
0, (byte) 3,
(byte) 8, (byte) 6, (byte) 0, (byte) 0, (byte)
0, (byte) 86,
(byte) 40, (byte) -75, (byte) -65, (byte) 0,
(byte) 0, (byte) 0,
(byte) 20, (byte) 73, (byte) 68, (byte) 65,
(byte) 84, (byte) 120,
(byte) -38, (byte) 99, (byte) 96, (byte)-128,
(byte)-128, (byte) -1,
(byte) 12, (byte) 48, (byte) 6, (byte) 8, (byte)
-96, (byte) 8,
(byte)-128, (byte) 8, (byte) 0, (byte)-107,
(byte)-111, (byte) 7,
(byte) -7, (byte) -64, (byte) -82, (byte) 8,
(byte) 0, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 73, (byte)
69, (byte) 78,
(byte) 68, (byte) -82, (byte) 66, (byte) 96,
(byte)-126
};
public static final long TINY_9PNG_CRC = 0x1148f987L;
// The XML document <x/> as binary-packed
with AAPT
public static final byte[] TINY_XML = new byte[]
{
(byte) 3, (byte) 0, (byte) 8, (byte) 0, (byte)
104, (byte) 0,
(byte) 0, (byte) 0, (byte) 1, (byte) 0, (byte)
28, (byte) 0,
(byte) 36, (byte) 0, (byte) 0, (byte) 0, (byte)
1, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte)
0, (byte) 0,
(byte) 0, (byte) 1, (byte) 0, (byte) 0, (byte)
32, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte)
0, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte)
1, (byte) 1,
(byte) 120, (byte) 0, (byte) 2, (byte) 1, (byte)
16, (byte) 0,
(byte) 36, (byte) 0, (byte) 0, (byte) 0, (byte)
1, (byte) 0,
(byte) 0, (byte) 0, (byte) -1, (byte) -1, (byte)
-1, (byte) -1,
(byte) -1, (byte) -1, (byte) -1, (byte) -1,
(byte) 0, (byte) 0,
(byte) 0, (byte) 0, (byte) 20, (byte) 0, (byte)
20, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte)
0, (byte) 0,
(byte) 0, (byte) 0, (byte) 3, (byte) 1, (byte)
16, (byte) 0,
(byte) 24, (byte) 0, (byte) 0, (byte) 0, (byte)
1, (byte) 0,
(byte) 0, (byte) 0, (byte) -1, (byte) -1, (byte)
-1, (byte) -1,
(byte) -1, (byte) -1, (byte) -1, (byte) -1,
(byte) 0, (byte) 0,
(byte) 0, (byte) 0
};
public static final long TINY_XML_CRC = 0xd7e65643L;
...
} |
ÉÏÃæ½ØÍ¼ÖÐ3¸öbyteÊý×éµÄ¶¨Òå¾ÍÊÇ×ÊԴѹËõ¹¤¾ßΪÎÞÓÃ×ÊÔ´ÌṩµÄÔ¤¶¨Òå°æ±¾£¬¿ÉÒÔ¿´³ö¶Ô.pngÌṩÁËTINY_PNG£¬
¶Ô.9.pngÌṩÁËTINY_9PNGÒÔ¼°¶Ô.xmlÌṩÁËTINY_XMLµÄÔ¤¶¨Òå°æ±¾¡£
×ÊԴѹËõ¹¤¾ßµÄÏêϸʹÓÿÉÒԲο¼Shrink Your Code and
Resources¡£×ÊԴѹËõ¹¤¾ßĬÈÏÊDzÉÓð²È«Ñ¹ËõģʽÀ´ÔËÐУ¬¿ÉÒÔͨ¹ý¿ªÆôÑϸñѹËõģʽÀ´´ïµ½¸üºÃµÄÊÝÉíЧ¹û¡£
Èç¹ûÏëÖªµÀÄÄЩ×ÊÔ´ÊÇÎÞÓõģ¬¿ÉÒÔͨ¹ý×ÊԴѹËõ¹¤¾ßµÄÊä³öÈÕÖ¾Îļþ ${project.buildDir}
/outputs /mappin g /release/resources.txtÀ´²é¿´¡£ÈçÏÂͼËùʾres/layout
/abc_activity _chooser_viewer .xml¾ÍÊÇÎÞÓõģ¬È»ºó±»Ô¤¶¨ÒåµÄ°æ±¾TINY_XMLËùÌæ»»£º

×ÊԴѹËõ¹¤¾ßÖ»ÊǰÑÎÞÓÃ×ÊÔ´Ìæ»»³ÉÔ¤¶¨Òå½ÏСµÄ°æ±¾£¬ÄÇÎÒÃÇÈçºÎɾ³ýÕâЩÎÞÓÃ×ÊÔ´ÄØ£¿Í¨³£µÄ×ö·¨ÊǽáºÏ×ÊԴѹËõ¹¤¾ßµÄÊä³öÈÕÖ¾£¬ÕÒµ½ÕâЩ×ÊÔ´²¢°ÑËüÃǽøÐÐɾ³ý¡£µ«ÔÚ±ÊÕßµÄÏîÄ¿ÖкܶàÎÞÓÃ×ÊÔ´ÊDZ»ÆäËü×é¼þ»òµÚÈý·½SDKËùÒýÈëµÄ£¬Èç¹û²ÉÓÃÕâÖÖÓÅ»¯·½Ê½»á´øÀ´ÕâЩSDKºóÆÚά»¤³É±¾µÄÔö¼Ó£¬Õë¶ÔÕâÖÖÇé¿ö±ÊÕßÊÇͨ¹ý²ÉÓÃÔÚresources.arscÖÐ×öÓÅ»¯À´½â¾öµÄ£¬ÏêÇé¿´ÏÂÃæ¡°resources.arscµÄÓÅ»¯¡±Ò»½ÚµÄ½éÉÜ¡£
ÓïÑÔ×ÊÔ´ÓÅ»¯
¸ù¾ÝApp×ÔÉíÖ§³ÖµÄÓïÑ԰汾ѡÓúÏÊʵÄÓïÑÔ×ÊÔ´£¬ÀýÈçʹÓÃÁËAppCompat£¬Èç¹û²»×öÈκÎÅäÖõϰ£¬×îÖÕAPK°üÖлá°üº¬AppCompatÖÐÏûÏ¢µÄËùÓÐÒÑ·ÒëÓïÑÔ×Ö·û´®£¬ÎÞÂÛÓ¦ÓÃµÄÆäÓಿ·ÖÊÇ·ñ·ÒëΪͬһÓïÑÔ£¬¿ÉÒÔͨ¹ýresConfigÀ´ÅäÖÃʹÓÃÄÄЩÓïÑÔ£¬´Ó¶øÈù¹½¨¹¤¾ßÒÆ³ýÖ¸¶¨ÓïÑÔÖ®ÍâµÄËùÓÐ×ÊÔ´¡£ÏÂͼÊǾßÌåµÄÅäÖÃʾÀý£º
android {
...
defaultConfig {
...
resConfigs "zh", "zh-rCN"
}
...
} |
Õë¶ÔΪ²»Í¬DPIËùÌṩµÄͼƬҲ¿ÉÒÔ²ÉÓÃÏàͬµÄ²ßÂÔ£¬ÐèÒªÕë¶Ô×ÔÉíµÄÄ¿±êÓû§ºÍÄ¿±êÉ豸×öÒ»¶¨µÄÑ¡Ôñ£¬¿ÉÒԲο¼
Support Only Specific DensitiesÀ´²Ù×÷¡£ÓÐ¹ØÆÁÄ»ÃܶȵÄÏêϸÐÅÏ¢£¬Çë²ÎÔÄScreen
Sizes and Densities¡£
¶Ô.soÎļþÒ²¿ÉÒÔ²ÉÓÃÀàËÆµÄ²ßÂÔ£¬±ÈÈç±ÊÕßµÄÏîÄ¿ÖÐÖ»±£ÁôÁËarmeabi°æ±¾µÄ.soÎļþ¡£
resources.arscµÄÓÅ»¯
Õë¶Ôresources.arsc£¬±ÊÕß³¢ÊÔ¹ýµÄÓÅ»¯ÊÖ¶ÎÈçÏ£º
¿ªÆô×ÊÔ´»ìÏý£»
¶ÔÖØ¸´µÄ×ÊÔ´½øÐÐÓÅ»¯£»
¶Ô±»shrinkResourcesÓÅ»¯µôµÄ×ÊÔ´½øÐд¦Àí¡£
ÏÂÃæ½«·Ö±ð¶ÔÕâЩÓÅ»¯ÊֶνøÐÐÕ¹¿ª½éÉÜ¡£
×ÊÔ´»ìÏý
ÔÚ±ÊÕßÁíһƪ¡¶ÃÀÍÅAndroid×ÊÔ´»ìÏý±£»¤Êµ¼ù¡·ÎÄÕÂÖнéÉÜÁ˲ÉÓöÔ×ÊÔ´»ìÏýµÄ·½Ê½À´±£»¤×ÊÔ´µÄ°²È«£¬Í¬Ê±Ò²Ìáµ½ÁËÕâÖÖ·½Ê½ÓÐÏÔÖøµÄÊÝÉíЧ¹û¡£±ÊÕßµ±Ê±ÊDzÉÓÃÐÞ¸ÄAAPTµÄÏà¹ØÔ´ÂëµÄ·½Ê½£¬ÕâÖÖ·½Ê½µÄÍ´µãÊÇÿ´ÎÉý¼¶Build
Tools¶¼ÒªÐÞ¸ÄÒ»´ÎAAPTÔ´Â룬ά»¤ÐԽϲĿǰ±ÊÕß²ÉÓÃÁË΢ÐÅ¿ªÔ´µÄ×ÊÔ´»ìÏý¿âAndResGuard£¬¾ßÌåµÄÔÀíºÍʹÓðïÖú¿ÉÒԲο¼°²×°°üÁ¢¼õ1M--΢ÐÅAndroid×ÊÔ´»ìÏý´ò°ü¹¤¾ß¡£
ÎÞÓÃ×ÊÔ´ÓÅ»¯
ÔÚÉÏÒ»½ÚÖнéÉÜÁË¿ÉÒÔͨ¹ýshrinkResources trueÀ´¿ªÆô×ÊԴѹËõ£¬×ÊԴѹËõ¹¤¾ß»á°ÑÎÞÓõÄ×ÊÔ´Ìæ»»³ÉÔ¤¶¨ÒåµÄ°æ±¾¶ø²»ÊÇÒÆ³ý£¬Èç¹û²ÉÓÃÈ˹¤ÒƳýµÄ·½Ê½»á´øÀ´ºóÆÚµÄά»¤³É±¾£¬ÕâÀï±ÊÕß²ÉÓÃÁËÒ»ÖֱȽÏÈ¡Çɵķ½Ê½£¬ÔÚAndroid¹¹½¨¹¤¾ßÖ´ÐÐpackage${flavorName}Task֮ǰͨ¹ýÐÞ¸ÄCompiled
ResourcesÀ´ÊµÏÖ×Ô¶¯È¥³ýÎÞÓÃ×ÊÔ´¡£
¾ßÌåÁ÷³ÌÈçÏ£º
ÊÕ¼¯×ÊÔ´°ü£¨Compiled ResourcesµÄ¼ò³Æ£©Öб»Ìæ»»µÄÔ¤¶¨Òå°æ±¾µÄ×ÊÔ´Ãû³Æ£¬Í¨¹ý²é¿´×ÊÔ´°ü£¨Zip¸ñʽ£©ÖÐÿ¸öZipEntryµÄCRC-32
checksumÀ´Ñ°ÕÒ±»Ìæ»»µÄÔ¤¶¨Òå×ÊÔ´£¬Ô¤¶¨Òå×ÊÔ´µÄCRC-32¶¨ÒåÔÚResourceUsageAnalyzer£¬ÏÂÃæÊÇËüÃǵ͍Òå¡£
// A 1x1 pixel
PNG of type BufferedImage.TYPE_BYTE_GRAY
public static final long TINY_PNG_CRC = 0x88b2a3b0L;
// A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB
with 9-patch markers
public static final long TINY_9PNG_CRC = 0x1148f987L;
// The XML document <x/> as binary-packed
with AAPT
public static final long TINY_XML_CRC = 0xd7e65643L; |
ͨ¹ýandroid-chunk-utils°Ñresources.arscÖжÔÓ¦µÄ¶¨ÒåÒÆ³ý£»
ɾ³ý×ÊÔ´°üÖжÔÓ¦µÄ×ÊÔ´Îļþ¡£
ÖØ¸´×ÊÔ´ÓÅ»¯
ĿǰÃÀÍÅAppÊÇÓɸ÷¸öÒµÎñÍŶӹ²Í¬¿ª·¢Íê³É£¬ÎªÁË·½±ã¸÷ÒµÎñÍŶӵĶÀÁ¢¿ª·¢£¬ÃÀÍÅApp½øÐÐÁËÆ½Ì¨»¯¸ÄÔì¡£¸ÄÔìʱ´æÔںܶà×ÊÔ´Îļþ£¨È磺drawable¡¢layoutµÈ£©±»²»Í¬µÄÒµÎñÍŶӶ¼¿½±´µ½×Ô¼ºµÄLibraryÏ£¬Í¬Ê±ÎªÁ˱ÜÃâÒý·¢×ÊÔ´¸²¸ÇµÄÎÊÌ⣬ÿ¸öÒµÎñÍŶӶ¼»áΪ×Ô¼ºµÄ×ÊÔ´ÎļþÃûÌí¼Óǰ׺¡£ÕâÑù¾Íµ¼ÖÂÁËÕâЩ×ÊÔ´ÎļþËäÈ»ÄÚÈÝÏàͬ£¬µ«ÒòΪÃû³ÆµÄ²»Í¬¶ø²»Äܱ»¸²¸Ç£¬×îÖÕ¶¼»á±»¼¯³Éµ½APK°üÖУ¬Õë¶ÔÕâÖÖÎÊÌâ±ÊÕß²ÉÓÃÁ˺ÍÇ°Ãæ¡°ÎÞÓÃ×ÊÔ´ÓÅ»¯¡±Ò»½ÚÖÐÃèÊöÀàËÆµÄ²ßÂÔ¡£
¾ßÌå²½ÖèÈçÏ£º
ͨ¹ý×ÊÔ´°üÖеÄÿ¸öZipEntryµÄ CRC-32 checksumÀ´É¸Ñ¡³öÖØ¸´µÄ×ÊÔ´£»
ͨ¹ýandroid-chunk-utilsÐÞ¸Ä resources.arsc£¬°ÑÕâÐ©ÖØ¸´µÄ×ÊÔ´¶¼Öض¨Ïòµ½Í¬Ò»¸öÎļþÉÏ£»
°ÑÆäËüÖØ¸´µÄ×ÊÔ´Îļþ´Ó×ÊÔ´°üÖÐɾ³ý¡£
´úÂëÆ¬¶Î£º
variantData.outputs.each
{
def apFile = it.packageAndroidArtifactTask .getResourceFile();
it.packageAndroidArtifactTask.doFirst {
def arscFile = new File (apFile.parentFile, " resources.arsc");
JarUtil.extractZipEntry (apFile, "resources.arsc",
arscFile);
def HashMap<String, ArrayList<DuplicatedEntry>>
duplicatedResources = findDuplicatedResources(apFile);
removeZipEntry (apFile, "resources.arsc");
if (arscFile.exists()) {
FileInputStream arscStream = null;
ResourceFile resourceFile = null;
try {
arscStream = new FileInputStream(arscFile);
resourceFile = ResourceFile.fromInputStream (arscStream);
List<Chunk> chunks = resourceFile.getChunks();
HashMap<String, String> toBeReplacedResourceMap
= new HashMap<String, String>(1024);
// ´¦Àíarsc²¢É¾³ýÖØ¸´×ÊÔ´
Iterator<Map.Entry< String, ArrayList< DuplicatedEntry>>>
iterator = duplicatedResources .entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ArrayList< DuplicatedEntry>>
duplicatedEntry = iterator.next();
// ±£ÁôµÚÒ»¸ö×ÊÔ´£¬ ÆäËû×ÊԴɾ³ýµô
for (def index = 1; index < duplicatedEntry.value.size();
++index) {
removeZipEntry (apFile, duplicatedEntry .value.get (index).name);
toBeReplacedResourceMap. put (duplicatedEntry.value.get (index).name,
duplicatedEntry.value.get(0).name);
}
}
for (def index = 0; index < chunks.size();
++index) {
Chunk chunk = chunks.get(index);
if (chunk instanceof ResourceTableChunk) {
ResourceTableChunk resourceTableChun k = (ResourceTableChunk)
chunk;
StringPoolChunk stringPoolChunk = resourceTableChunk.getStringPool();
for (def i = 0; i < stringPoolChunk.stringCount;
++i) {
def key = stringPoolChunk.getString(i);
if (toBeReplacedResourceMap .containsKey(key))
{
stringPoolChunk.setString (i, toBeReplacedResourceMap .get(key));
}
}
}
}
} catch (IOException ignore) {
} catch (FileNotFoundException ignore) {
} finally {
if (arscStream != null) {
IOUtils.closeQuietly(arscStream);
}
arscFile.delete();
arscFile << resourceFile.toByteArray();
addZipEntry(apFile, arscFile);
}
}
}
} |
ͨ¹ýÕâÖÖ·½Ê½¿ÉÒÔÓÐЧ¼õÉÙÖØ¸´×ÊÔ´¶Ô°üÌå´óСµÄÓ°Ï죬ͬʱÕâÖÖ²Ù×÷·½Ê½¶Ô¸÷ÒµÎñÍŶÓ͸Ã÷£¬Ò²²»»áÔö¼Óе÷Ïàͬ×ÊÔ´ÈçºÎ±»²»Í¬ÒµÎñÍŶӸ´Óõijɱ¾¡£
×ܽá
ÉÏÊö¾ÍÊÇÎÒÃÇĿǰÔÚAPKÊÝÉí·½ÃæµÄ×öµÄһЩ³¢ÊԺͻýÀÛ£¬¿ÉÒÔ¸ù¾Ý×ÔÉíÇé¿öÈ¡ÉáʹÓᣵ±È»ÎÒÃÇ»¹¿ÉÒÔ²ÉȡһЩ°´Ðè¼ÓÔØµÄ²ßÂÔÀ´¼õÉÙ°²×°°üµÄÌå»ý¡£×îºóÌáÒ»µã£¬¿³µô²»±ØÒªµÄ¹¦ÄܲÅÊǰ²×°°üÊÝÉíµÄ³¬¼¶´óÕС£Ò»¸öºÃµÄAppµÄ±ê×¼ÓÐºÜ¶à·½Ãæ£¬µ«Ìṩ¾¡¿ÉÄÜСµÄ°²×°°üÊÇÆäÖÐÒ»¸öÖØÒªµÄ·½Ã棬ÕâÒ²ÊǶÔÎÒÃÇAndroid¿ª·¢ÕßÈËÔ±×ÔÉíµÄÌá³öµÄ»ù±¾ÒªÇó£¬ÒªÊ±¿Ì±£³ÖÁ¼ºÃµÄ±à³Ìϰ¹ßºÍ¶Ô°üÌå»ýÃôÈñµÄÐá¾õ¡£ |