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: astore6
533: jsr
539
536: aload
6
538: athrow
539: astore7
541: aload
4
543: ifnull574
能够看到,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,一劳永逸!
)
Android项目打包开启proguard的混淆优化带来的问题的更多相关文章
- Win7平台下React-Native开发之Android项目打包发布流程
一.bundle文件 React-Native开发步骤中,有一个步骤是使用命令 react-native start 去启动一个基于Node.js的服务,名字为packager.这个packager的 ...
- Android项目外接高德地图代码混淆注意事项
如今好多项目中都加入了第三方jar包,可是最大的问题就是打包的时候代码混淆报错,下面是高德地图混淆报错解决方式: 在proguard-project.txt中加入例如以下代码: -libraryjar ...
- Eclipse将android项目打包jar文件
Eclipse+android打包jar文件 蔡建良 2016-3-12 以Android-SlideExpandableListView开源框架为例,将源码Library打包成jar文件并包含R.c ...
- Android项目打包成APK文件
第一步:右键单击该项目选择Export项目 显演示样例如以下界面:选择Exprot Android Application 第二步:输入项目名称,默认的情况下是原始的项目名称 下一步: 点击 Crea ...
- Android项目打包遇com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
查了网上很多处理都是在项目的gradle.properties中添加下面的配置,再编译就通过了: android.enableAapt2=false
- Android 将Android项目打包成aar文件
参考资料:https://blog.csdn.net/csdn_mm/article/details/78364444
- apk反编译(6)ProGuard 工具 android studio版官方教程[作用,配置,解混淆,优化示例]
ProGuard In this document Enabling ProGuard (Gradle Builds) Configuring ProGuard Examples Decoding O ...
- Android ProGuard代码混淆技术详解
前言 受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...
- android菜鸟学习笔记16----Android项目打包安装过程(Run as Android Application)
右击项目名称,Run as Android Appication之后,Android项目打包安装过程: 1.打包生成.apk文件: 1)把源码中的.java文件编译生成.class文件 2)将所有的. ...
随机推荐
- 用dTree组件生成无限级导航树
在做管理系统时不可避免要用到导航树,这种东西只要一次做好,就可以随处运行,目前比较好的组件是dTree,原则上可以达到无限级,当然实际运行中4,5级就已经很多了,dTree的速度还是不错的,而且是J ...
- Hibernate(五)——面向对象查询语言和锁
Hibernate做了数据库中表和我们实体类的映射,使我们不必再编写sql语言了.但是有时候查询的特殊性,还是需要我们手动来写查询语句呢,Hibernate框架为了解决这个问题给我们提供了HQL(Hi ...
- XCode中在提示窗体中对已弃用的API接口画上红线
当我们在XCode中写程序时会不断的出现相关API提示窗体,那敲起来是一个爽啊. 有时候会看到一些API已经弃用了被画上红色的横线.说明该接口已经被弃用,仍保留,但不建议使用,对弃用API实现画横线事 ...
- hdu1171 Big Event in HDU 01-背包
转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1171 Problem ...
- android——屏幕适配大全(转载)
http://my.oschina.net/u/2008084/blog/496161 一.适配可行性 早在Android设计之初就考虑到了这一点,为了让app适应标准or山寨屏幕,google已经有 ...
- Eclipse控制台显示Tomcat日志
今天看一篇学习Struts的博文,文章里面提到从生成的日志,结果,怎么鼓捣都看不到.心情也跟着烦躁了.于是晚饭后出去散步,冷静一下,然后决定晚上一定搞掂这个问题.这不,搞掂了,写篇博文记录一下. St ...
- 详解一名合格PHP工程师应该具备的基本知识结构
在学习php时的一些小沉淀,供童鞋们欣赏哈.. 1.掌握语言本身的语法,熟悉常用类库是必须的: 2.面向对象最为当今的主流也是必须掌握的: 3.常用设计模式必须掌握,设计模式是前辈的总结.经验: 4. ...
- apache和tomcat的区别
Web服务器父与子 Apache和Tomcat区别 经常在用Apache和Tomcat等这些服务器时,你总感觉还是不清楚他们之间有什么关系.在用Tomcat的时候总出现Apache,总感到迷惑,到底谁 ...
- full-background.js
$(window).on('load', function () { $(window).trigger('resize'); }); $(function () { var $window = $( ...
- [原]C语言实现的快速排序,采用分治策略,递归实现
#include<stdio.h> #define LEN 8 int a[LEN] = { 5, 2, 4, 7, 1, 3, 2, 6 }; int Partition(int a[] ...