Android项目打包开启proguard的混淆优化带来的问题
[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執(zhí)行結(jié)束后,會輸出下面文件:
dump.txt 描寫敘述.apk文件里全部類文件間的內(nèi)部結(jié)構(gòu)
mapping.txt 列出了原始的類,方法和字段名與混淆后代碼間的映射。這個(gè)文件非常重要。當(dāng)你從release版本號中收到一個(gè)bug報(bào)告時(shí)。能夠用它來翻譯被混淆的代碼。
seeds.txt 列出了未被混淆的類和成員
usage.txt 列出了從.apk中刪除的代碼
查看我們的seeds.txt:
com.alibaba.mobileim.channel.service.SOManager: boolean _loadFile()
說明,SOManager類的_loadFile()方法是沒有被混淆掉的。
2.看上去是處理SOManager類的_loadFile()方法的第536條指令的時(shí)候出的問題。反編譯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:jsr539
? ?536:aload6
? ?538:athrow
? ?539:astore7
? ?541:aload4
? ?543:ifnull574
能夠看到,536行的字節(jié)碼是:
? ?536:aload6
就是處理這條指令的時(shí)候拋出的空指針。至此,基本能夠斷定問題出在字節(jié)碼上!
3.那可能是什么原因?qū)е碌哪?#xff1f;
(1)是不是sdk使用的javac的版本號不正確,導(dǎo)致proguard無法處理里面的字節(jié)碼。可是maven庫里面的jar包不是開發(fā)人員手動(dòng)上傳的,因此排除。
(2)是不是proguard的版本號太低?從官網(wǎng)的1.0一直試到了最新的4.11,還是不行。
沒辦法繼續(xù)看官網(wǎng)山的文檔。在http://proguard.sourceforge.net/#manual/usage.html頁面,看到Optimization Options這一章節(jié)的前三個(gè)選項(xiàng):
-dontoptimize
Specifies not to optimize the input class files. By default, optimization is enabled; all methods are optimized at a bytecode level.
不優(yōu)化輸入的class文件。
默認(rèn)情況下是啟用優(yōu)化的。對全部的方法都會在字節(jié)碼級別進(jìn)行優(yōu)化!
-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.
在細(xì)粒度級別設(shè)置啟用和禁用的優(yōu)化選項(xiàng)。僅僅有當(dāng)啟用優(yōu)化的時(shí)候才可用。這是一個(gè)專家級選項(xiàng)!
-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.
這個(gè)看源代碼貌似是依據(jù)optimizationpasses做了一個(gè)循環(huán),進(jìn)行多次優(yōu)化:
proguard.ant.ProGuardTask.java:
#---------------
# 混淆優(yōu)化
#---------------
-optimizationpasses 7
也就是說我們是開啟了混淆優(yōu)化的,這就說會對字節(jié)碼做改動(dòng)。極有可能是這個(gè)原因!換用-dontoptimize試一下,果然能夠了!
4.詳細(xì)proguard對字節(jié)碼會做那些優(yōu)化呢?
看這個(gè)文檔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.
真是不看不知道,一看嚇一跳!居然會做這么多種優(yōu)化!
然后看到這個(gè)頁面: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.
大概意思是說,有的優(yōu)化可能會導(dǎo)致空指針。就像我們的樣例中,就是拋出了空指針!
然后在http://proguard.sourceforge.net/index.html#manual/examples.html這個(gè)頁面,看到一行:
-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).
也就是說有的優(yōu)化Dalvik是不支持的。所以要排除掉!
所以,非常可能是有些優(yōu)化我們沒有排除掉!
看一下我們的配置文件中面設(shè)置的優(yōu)化選項(xiàng):
# ----優(yōu)化選項(xiàng)----
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
這幾個(gè)選項(xiàng)詳細(xì)是干嘛的?
參考: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).?
那么。詳細(xì)有多少種能夠使用的優(yōu)化選項(xiàng)呢?看這里: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.
相應(yīng)的源代碼proguard.optimize.Optimizer.java:
非常不幸的是,我把所有的優(yōu)化選項(xiàng)所有排除掉以后,還是無法打包!
所以,僅僅能懷疑我們項(xiàng)目的proguard的版本號和上面列出的優(yōu)化選項(xiàng)不匹配!更換最新的proguard4.11的jar包,然后挨個(gè)選項(xiàng)的試,終于試出來了:
-optimizations !code/simplification/*,!field/*,!class/merging/*,!method/removal/parameter,!method/propagation/*,!method/marking/static,!class/unboxing/enum,!code/removal/advanced,!code/allocation/variable
總結(jié)一下:
(1)ProGuard不光能做混淆,還能做代碼優(yōu)化。
(2)ProGuard不是專門為android的Dalvik使用的。
(3)就算是Sun的Hotspot JVM也會有不支持的優(yōu)化選項(xiàng)。
(4)不知道未來哪個(gè)優(yōu)化選項(xiàng)會導(dǎo)致打包通只是,而我們主要是使用ProGuard的混淆功能。干脆-dontoptimize,一勞永逸!
)
轉(zhuǎn)載于:https://www.cnblogs.com/zfyouxi/p/5224719.html
總結(jié)
以上是生活随笔為你收集整理的Android项目打包开启proguard的混淆优化带来的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【README】回溯算法基本框架
- 下一篇: 20个常用Linux性能监控工具/命令