`

Android项目打包开启proguard的混淆优化带来的问题

阅读更多
1.引入一个sdk以后,打包报错: 
[INFO] Unexpected error while evaluating instruction: 
[INFO]   Class       = [com/alibaba/mobileim/channel/service/SOManager] 
[INFO]   Method      = [_loadFile()Z] 
[INFO]   Instruction = [536] aload v6 
[INFO]   Exception   = [java.lang.NullPointerException] (null) 
[INFO] Unexpected error while performing partial evaluation: 
[INFO]   Class       = [com/alibaba/mobileim/channel/service/SOManager] 
[INFO]   Method      = [_loadFile()Z] 
[INFO]   Exception   = [java.lang.NullPointerException] (null) 
[INFO] java.lang.NullPointerException) 

我们知道ProGuard运行结束后,会输出以下文件: 
dump.txt 描述.apk文件中所有类文件间的内部结构 
mapping.txt 列出了原始的类,方法和字段名与混淆后代码间的映射。这个文件很重要,当你从release版本中收到一个bug报告时,可以用它来翻译被混淆的代码。 
seeds.txt 列出了未被混淆的类和成员 
usage.txt 列出了从.apk中删除的代码 

查看我们的seeds.txt: 
com.alibaba.mobileim.channel.service.SOManager: boolean _loadFile() 
说明,SOManager类的_loadFile()方法是没有被混淆掉的。 

2.看上去是处理SOManager类的_loadFile()方法的第536条指令的时候出的问题,反编译SOManager.class: 
   507: invokevirtual #112; //Method java/io/IOException.getMessage:()Ljava/lang/String; 
   510: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
   513: ldc #110; //String   
   515: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
   518: getstatic #41; //Field logInfo:Ljava/lang/StringBuffer; 
   521: invokevirtual #100; //Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 
   524: invokevirtual #19; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
   527: invokespecial #55; //Method java/lang/UnsatisfiedLinkError."<init>":(Ljava/lang/String;)V 
   530: athrow 
   531: astore 6 
   533: jsr 539 
   536: aload 6 
   538: athrow 
   539: astore 7 
   541: aload 4 
   543: ifnull 574 
可以看到,536行的字节码是: 
   536: aload 6 
就是处理这条指令的时候抛出的空指针。至此,基本可以断定问题出在字节码上! 

3.那可能是什么原因导致的呢? 
(1)是不是sdk使用的javac的版本不对,导致proguard无法处理里面的字节码,但是maven库里面的jar包不是开发者手动上传的,因此排除。 
(2)是不是proguard的版本太低?从官网的1.0一直试到了最新的4.11,还是不行。 

没办法继续看官网山的文档,在http://proguard.sourceforge.net/#manual/usage.html页面,看到Optimization Options这一章节的前三个选项: 

-dontoptimize 
Specifies not to optimize the input class files. By default, optimization is enabled; all methods are optimized at a bytecode level. 
不优化输入的class文件。默认情况下是启用优化的,对所有的方法都会在字节码级别进行优化! 

-optimizations optimization_filter 
Specifies the optimizations to be enabled and disabled, at a more fine-grained level. Only applicable when optimizing. This is an expert option. 
在细粒度级别设置启用和禁用的优化选项,只有当启用优化的时候才可用。这是一个专家级选项! 

-optimizationpasses n 
Specifies the number of optimization passes to be performed. By default, a single pass is performed. Multiple passes may result in further improvements.  
If no improvements are found after an optimization pass, the optimization is ended. Only applicable when optimizing. 
这个看源码貌似是根据optimizationpasses做了一个循环,进行多次优化: 
proguard.ant.ProGuardTask.java: 
if (configuration.optimize){ 
for (int optimizationPass = 0;optimizationPass < configuration.optimizationPasses;optimizationPass++){ 
if (!optimize()){ 
   // Stop optimizing if the code doesn't improve any further. 
   break; 

// Shrink again, if we may. 
if (configuration.shrink){ 
   // Don't print any usage this time around. 
   configuration.printUsage       = null; 
   configuration.whyAreYouKeeping = null; 
   shrink(); 




也就是说Optimization Options会对字节码做修改,我们的proguard.cfg的一开头就是: 
#--------------- 
# 混淆优化 
#--------------- 
-optimizationpasses 7 

也就是说我们是开启了混淆优化的,这就说会对字节码做修改,极有可能是这个原因!换用-dontoptimize试一下,果然可以了! 

4.具体proguard对字节码会做那些优化呢? 
看这个文档http://proguard.sourceforge.net/#FAQ.html: 

What kind of optimizations does ProGuard support? 
Apart from removing unused classes, fields, and methods in the shrinking step, ProGuard can also perform optimizations at the bytecode level, inside methods. Thanks to techniques like control flow analysis, data flow analysis, partial evaluation, and liveness analysis, ProGuard can: 
Evaluate constant expressions. 
Remove unnecessary field accesses and method calls. 
Remove unnecessary branches. 
Remove unnecessary comparisons and instanceof tests. 
Remove unused code blocks. 
Merge identical code blocks. 
Reduce variable allocation. 
Remove write-only fields and unused method parameters. 
Inline constant fields, method parameters, and return values. 
Inline methods that are short or only called once. 
Make methods private, static, and final when possible. 
Make classes static and final when possible. 
Replace interfaces that have single implementations. 
Perform over 200 peephole optimizations, like replacing ...*2 by ...<<1. 
Optionally remove logging code. 
The positive effects of these optimizations will depend on your code and on the virtual machine on which the code is executed. Simple virtual machines may benefit more than advanced virtual machines with sophisticated JIT compilers. At the very least, your bytecode may become a bit smaller. 
Some notable optimizations that aren't supported yet: 
Moving constant expressions out of loops. 
Optimizations that require escape analysis. 

真是不看不知道,一看吓一跳!竟然会做这么多种优化! 

然后看到这个页面:http://proguard.sourceforge.net/#manual/limitations.html 

When using ProGuard, you should be aware of a few technical issues, all of which are easily avoided or resolved: 
For best results, ProGuard's optimization algorithms assume that the processed code never intentionally throws NullPointerExceptions or  
ArrayIndexOutOfBoundsExceptions, or even OutOfMemoryErrors or StackOverflowErrors, in order to achieve something useful.  
For instance, it may remove a method call myObject.myMethod() if that call wouldn't have any effect.  
It ignores the possibility that myObject might be null, causing a NullPointerException.  
In some way this is a good thing: optimized code may throw fewer exceptions. Should this entire assumption be false,  
you'll have to switch off optimization using the -dontoptimize option. 

ProGuard's optimization algorithms currently also assume that the processed code never creates busy-waiting loops without at least testing on a volatile field. Again, it may remove such loops. Should this assumption be false, you'll have to switch off optimization using the -dontoptimize option. 
If an input jar and a library jar contain classes in the same package, the obfuscated output jar may contain class names that overlap with class names in the library jar. This is most likely if the library jar has been obfuscated before, as it will then probably contain classes named 'a', 'b', etc. Packages should therefore never be split across input jars and library jars. 
When obfuscating, ProGuard writes out class files named "a.class", "b.class", etc. If a package contains a large number of classes, ProGuard may also write out "aux.class". Inconveniently, Windows refuses to create files with this reserved name (among a few other names). It's generally better to write the output to a jar, in order to avoid such problems. 

大概意思是说,有的优化可能会导致空指针,就像我们的例子中,就是抛出了空指针! 

然后在http://proguard.sourceforge.net/index.html#manual/examples.html这个页面,看到一行: 

-optimizations !code/simplification/arithmetic 

The -optimizations option disables some arithmetic simplifications that Dalvik 1.0 and 1.5 can't handle.  
Note that the Dalvik VM also can't handle aggressive overloading (of static fields). 

也就是说有的优化Dalvik是不支持的,所以要排除掉!所以,很可能是有些优化我们没有排除掉! 

看一下我们的配置文件里面设置的优化选项: 

# ----优化选项---- 
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 

这几个选项具体是干嘛的? 

参考:https://groups.google.com/forum/#!topic/android-developers/v_o0AQ7o8gI 

#1: !code/simplification/arithmetic: This removes things like turning  
"3 + 3" into "6".  A shame, but understandable, because there are much  
more complicated optimizations to the byte code that Dalvik doesn't  
handle well.  This one is completely understood.  

#2: !field/*: This refers to the following:  
field/removal/writeonly - Removes write-only fields.  
field/marking/private - Marks fields as private, whenever possible.  
field/propagation/value - Propagates the values of fields across  
methods.  

#3: !class/merging/*: This disables merging two or more classes  
horizontally or vertically (in the same class hierarchy).  

那么,具体有多少种可以使用的优化选项呢?看这里:http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/optimizations.html 

http://proguard.sourceforge.net->manual-> ref card->-optimizations optimization_filter 

Optimizations 
The optimization step of ProGuard can be switched off with the -dontoptimize option. For more fine-grained control over individual optimizations, experts can use the -optimizations option, with a filter based on the optimization names listed below. The filter works like any filter in ProGuard. 
The following wildcards are supported: 
? matches any single character in an optimization name. 
* matches any part of an optimization name. 
An optimization that is preceded by an exclamation mark '!' is excluded from further attempts to match with subsequent optimization names in the filter. Make sure to specify filters correctly, since they are not checked for potential typos. 
For example, "code/simplification/variable,code/simplification/arithmetic" only performs the two specified peephole optimizations. 
For example, "!method/propagation/*" performs all optimizations, except the ones that propagate values between methods. 
For example, "!code/simplification/advanced,code/simplification/*" only performs all peephole optimizations. 
Some optimizations necessarily imply other optimizations. These are then indicated. Note that the list is likely to change over time, as optimizations are added and reorganized. 
class/marking/final 
Marks classes as final, whenever possible. 
class/unboxing/enum 
Simplifies enum types to integer constants, whenever possible. 
class/merging/vertical 
Merges classes vertically in the class hierarchy, whenever possible. 
class/merging/horizontal 
Merges classes horizontally in the class hierarchy, whenever possible. (? code/removal/advanced) 
field/removal/writeonly 
Removes write-only fields. 
field/marking/private 
Marks fields as private, whenever possible.(? code/simplification/advanced) 
field/propagation/value 
Propagates the values of fields across methods. 
method/marking/private 
Marks methods as private, whenever possible (devirtualization).(? code/removal/advanced) 
method/marking/static 
Marks methods as static, whenever possible (devirtualization). 
method/marking/final 
Marks methods as final, whenever possible.(? code/removal/advanced) 
method/removal/parameter 
Removes unused method parameters.(? code/simplification/advanced) 
method/propagation/parameter 
Propagates the values of method parameters from method invocations to the invoked methods.(? code/simplification/advanced) 
method/propagation/returnvalue 
Propagates the values of method return values from methods to their invocations. 
method/inlining/short 
Inlines short methods. 
method/inlining/unique 
Inlines methods that are only called once. 
method/inlining/tailrecursion 
Simplifies tail recursion calls, whenever possible. 
code/merging 
Merges identical blocks of code by modifying branch targets. 
code/simplification/variable 
Performs peephole optimizations for variable loading and storing. 
code/simplification/arithmetic 
Performs peephole optimizations for arithmetic instructions. 
code/simplification/cast 
Performs peephole optimizations for casting operations. 
code/simplification/field 
Performs peephole optimizations for field loading and storing.(? code/removal/simple) 
code/simplification/branch 
Performs peephole optimizations for branch instructions. 
code/simplification/string 
Performs peephole optimizations for constant strings.(best used with code/removal/advanced) 
code/simplification/advanced 
Simplifies code based on control flow analysis and data flow analysis.(? code/removal/exception) 
code/removal/advanced 
Removes dead code based on control flow analysis and data flow analysis.(? code/removal/exception) 
code/removal/simple 
Removes dead code based on a simple control flow analysis. 
code/removal/variable 
Removes unused variables from the local variable frame. 
code/removal/exception 
Removes exceptions with empty try blocks. 
code/allocation/variable 
Optimizes variable allocation on the local variable frame. 


对应的源码proguard.optimize.Optimizer.java: 
private static final String CLASS_MARKING_FINAL            = "class/marking/final"; 
    private static final String CLASS_UNBOXING_ENUM            = "class/unboxing/enum"; 
    private static final String CLASS_MERGING_VERTICAL         = "class/merging/vertical"; 
    private static final String CLASS_MERGING_HORIZONTAL       = "class/merging/horizontal"; 
    private static final String FIELD_REMOVAL_WRITEONLY        = "field/removal/writeonly"; 
    private static final String FIELD_MARKING_PRIVATE          = "field/marking/private"; 
    private static final String FIELD_PROPAGATION_VALUE        = "field/propagation/value"; 
    private static final String METHOD_MARKING_PRIVATE         = "method/marking/private"; 
    private static final String METHOD_MARKING_STATIC          = "method/marking/static"; 
    private static final String METHOD_MARKING_FINAL           = "method/marking/final"; 
    private static final String METHOD_REMOVAL_PARAMETER       = "method/removal/parameter"; 
    private static final String METHOD_PROPAGATION_PARAMETER   = "method/propagation/parameter"; 
    private static final String METHOD_PROPAGATION_RETURNVALUE = "method/propagation/returnvalue"; 
    private static final String METHOD_INLINING_SHORT          = "method/inlining/short"; 
    private static final String METHOD_INLINING_UNIQUE         = "method/inlining/unique"; 
    private static final String METHOD_INLINING_TAILRECURSION  = "method/inlining/tailrecursion"; 
    private static final String CODE_MERGING                   = "code/merging"; 
    private static final String CODE_SIMPLIFICATION_VARIABLE   = "code/simplification/variable"; 
    private static final String CODE_SIMPLIFICATION_ARITHMETIC = "code/simplification/arithmetic"; 
    private static final String CODE_SIMPLIFICATION_CAST       = "code/simplification/cast"; 
    private static final String CODE_SIMPLIFICATION_FIELD      = "code/simplification/field"; 
    private static final String CODE_SIMPLIFICATION_BRANCH     = "code/simplification/branch"; 
    private static final String CODE_SIMPLIFICATION_STRING     = "code/simplification/string"; 
    private static final String CODE_SIMPLIFICATION_ADVANCED   = "code/simplification/advanced"; 
    private static final String CODE_REMOVAL_ADVANCED          = "code/removal/advanced"; 
    private static final String CODE_REMOVAL_SIMPLE            = "code/removal/simple"; 
    private static final String CODE_REMOVAL_VARIABLE          = "code/removal/variable"; 
    private static final String CODE_REMOVAL_EXCEPTION         = "code/removal/exception"; 
    private static final String CODE_ALLOCATION_VARIABLE       = "code/allocation/variable"; 

那肯定是这里面的有些优化选项导致了无法打包这个问题! 

很不幸的是,我把所有的优化选项全部排除掉以后,还是无法打包! 

所以,只能怀疑我们项目的proguard的版本和上面列出的优化选项不匹配!更换最新的proguard4.11的jar包,然后挨个选项的试,最终试出来了: 

-optimizations !code/simplification/*,!field/*,!class/merging/*,!method/removal/parameter,!method/propagation/*,!method/marking/static,!class/unboxing/enum,!code/removal/advanced,!code/allocation/variable 

总结一下: 
(1)ProGuard不光能做混淆,还能做代码优化。 
(2)ProGuard不是专门为android的Dalvik使用的。 
(3)就算是Sun的Hotspot JVM也会有不支持的优化选项。 
(4)不知道未来哪个优化选项会导致打包通不过,而我们主要是使用ProGuard的混淆功能,干脆-dontoptimize,一劳永逸!)

 

分享到:
评论

相关推荐

    idfc-proguard混淆优化注解形式demo3

    在这个“idfc-proguard混淆优化注解形式demo3”项目中,我们将探讨如何利用Proguard与SSH(Struts2、Spring4、Hibernate4)框架集成,通过注解方式实现优化和混淆。 首先,SSH是一个常见的企业级Java Web开发框架...

    【android开发】混淆打包proguard模板

    ProGuard是一款强大的Java字节码混淆、优化、预校验和分析工具,它可以为我们的Android项目提供必要的保护,防止恶意逆向工程分析。本文将深入探讨"【android开发】混淆打包proguard模板"的相关知识点。 一、...

    proguard程序混淆器

    ProGuard可以方便地与构建工具如Ant、Maven、Gradle集成,自动化混淆、优化和打包流程。在Android Studio中,ProGuard配置通常在`build.gradle`文件中完成,通过`minifyEnabled true`开启混淆,并指定配置文件路径...

    ant proguard 混淆编译 之Eclipse-Idea

    在Android开发中,为了保护应用的安全性和优化代码,开发者通常会使用ProGuard工具进行混淆编译。本篇文章将聚焦于如何在Eclipse和IntelliJ IDEA(Idea)这两个不同的集成开发环境中配置并使用ProGuard,以实现对Ant...

    Android添加Proguard混淆支持完整实例

    以下是一个基于Android SDK 1.5和Eclipse 3.5的Proguard混淆支持完整实例的详细说明。 首先,我们需要理解Proguard的基本配置。Proguard的配置文件通常命名为`proguard-project.txt`或`proguard.cfg`,位于项目的根...

    J2ME混淆打包工具proguard4.4.zip

    **J2ME混淆打包工具ProGuard 4.4详解** J2ME(Java 2 Micro Edition)是Java平台的一个子集,主要用于嵌入式设备和移动设备上的应用程序开发。在开发过程中,为了保护代码安全、防止反编译,开发者通常会使用混淆...

    proguard4.4混淆器

    ProGuard 是一款强大的开源代码混淆、优化和预校验工具,适用于Java和Android开发。在标题中提到的"proguard4.4混淆器"指的是ProGuard的一个特定版本,即4.4版。这个版本主要针对J2ME(Java 2 Micro Edition)和...

    proguard界面混淆版

    6. **输出**:最后,ProGuard将混淆和优化后的结果输出到指定的目录,供打包成APK或JAR使用。 **注意事项** 1. **保留关键类和方法**:混淆时必须确保关键的类和方法不被混淆,例如那些公开的API接口,否则可能...

    Android Java代码混淆工具:ProGuard简介及使用.docx

    在Android开发中,ProGuard是最广泛使用的Java代码混淆工具之一,它不仅能够混淆代码,还能够对代码进行优化,减少APK的大小,提高运行效率。 #### ProGuard的工作原理 ProGuard通过对类、方法和字段的名称进行...

    android activity,java打包架包混淆经验.docx

    在Android应用开发中,对项目进行打包和混淆是非常重要的步骤,可以有效保护代码安全,减少体积,提升性能。本文将详细介绍如何使用ProGuard 5.1进行打包混淆,特别是涉及到Activity、JNI、方法以及第三方库的处理。...

    ProGuard混淆与dex2jar反编译工具

    1. **ProGuard混淆**:在Android Studio中,可以在`build.gradle`文件中配置`ProGuard`规则。添加混淆规则后,每次构建APK时,`ProGuard`会自动执行混淆操作。 ```groovy android { buildTypes { release { ...

    打包工具-混淆器

    在给定的压缩包文件中,很可能包含了一个易于使用的混淆打包工具,如ProGuard或R8,帮助开发者快速且安全地完成项目的打包工作。使用这些工具,开发者可以更加放心地将代码部署到生产环境中,而不必担心源代码泄露的...

    android项目打包签名及其项目如何发布等详细步骤.zip

    3. ProGuard与R8混淆:为了保护代码安全和优化体积,可以启用ProGuard或R8进行代码混淆。 4. 多渠道打包:如果需要为不同设备或分发渠道创建定制的APK,可以通过Gradle的Product Flavors功能实现。 四、JavaApk...

    android-sdk目录下tools文件夹下的proguard文件夹

    ProGuard是Android开发中一个非常重要的工具,它主要用于优化、混淆、压缩和移除Java代码,以提升应用程序的安全性和性能。在Android SDK的`tools`文件夹下,ProGuard的相关文件提供了实现这些功能的配置和执行环境...

    androidstudio项目打包

    ### Android Studio 项目打包详解 #### 一、项目打包的重要性 在完成了Android应用程序的开发之后,最终的目标是将其发布到应用商店或者分发给用户。为了实现这一目标,我们需要将项目打包成Android安装包文件...

    Android-Androidstudio多module混淆成一个Jar

    本文将详细介绍如何在Android Studio中将多个Module混淆并打包成一个Jar文件。 1. **创建Module** 首先,确保你已经拥有多个独立的Android Studio Module。每个Module代表一个独立的功能或库。在Android Studio中,...

    android项目打包下载

    通过"android项目打包下载2",开发者不仅可以学习到Android应用的基本构建流程,还可以深入了解如何管理和优化项目资源,以及如何准备应用发布。这有助于提升开发技能,更好地理解和实践Android开发。

    android打包混淆pdf

    本文将详细介绍Android打包混淆的技巧、工具使用以及反混淆的相关内容。 首先,混淆是一种代码保护机制,它通过将类名、函数名、成员变量名等替换为无意义的名称,来达到代码难以阅读的效果。混淆工具普遍采用的...

    Android 签名混淆打包文档

    ### Android签名混淆打包详解 #### 一、签名与混淆打包的重要性 在Android应用开发过程中,签名与混淆打包是非常重要的步骤。签名确保了应用的身份唯一性和后续版本更新的一致性,而混淆则增强了应用的安全性,保护...

    proguard-v4.11.rar

    ProGuard是一款广泛应用于Java和Android开发的代码混淆、优化、预校验和打包工具。在Android开发中,ProGuard主要用于混淆代码,使反编译变得困难,从而提高应用的安全性。在给定的"proguard-v4.11.rar"压缩包中,...

Global site tag (gtag.js) - Google Analytics