Proguard使用assumenosideeffects
关键字可以很好地删除一些不重要的Log调用。但是,它对于一些较为复杂的Log调用处理得并不好。
所谓“较为复杂”,就是指不止包含一个简单字符串的Log调用。比如:Log.i(TAG,"velocity="+velocity)
。
Proguard会保留new StringBuilder("velocity=")
以及连接该值的变量,而仅仅只是移除这行代码中的Log调用。
这些字符串将会占用内存和CPU周期,并且还有助于黑客理解代码。
为了解决这个问题,我会把每个非常规的调试Log调用用if(BuildConfig.DEBUG){...}
语句包裹起来。
但是这样做每次都需要打这样的包裹语句,非常繁琐且容易出错,也不符合DRY(Don't repeat yourself)原则。
那么,是否有一种方法可以完全删除某个方法(或通过其他方式),包括所有调用该方法的方法?
例如:
@proguard_purge
public static void vanishingDebug(String whatever) {
Log.i(TAG,whatever);
}
那么混淆器会使该方法消失,同时也会递归地消除所有对此方法的调用吗?
详细说明
混淆将优化代码并删除未使用或被排除的方法。
但是代码编译会在方法调用之前生成额外的字节码,这些先行代码在混淆后将会保留,留下类似以下的代码:
new StringBuilder("velocity=").append(a)
(假设在编译时无法确定a
。要进行测试,请使用velocity=Math.random();
)
这使得混淆代码变得非常容易理解。
要重现此问题,您需要安装dex2jar将apk转换为jar,并JAD将jar转换为java代码。
您将看到实际留下的内容,会感到恐惧。
示例
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("TAG", "Simple Comment"); // << Disappears well!
double index = Math.random();
index++;
Log.i("TAG2", "log_index=" + index); // << hmm... (!)
// class with only log calls inside
new ReferencedClass();
// simple method call
MyLogger.notLog("no_log" + index); // << stays, as expected
// simple method call with only Log call inside (aka "Log Wrapper")
MyLogger.log("log" + index); // << stays, as expected
Log.i("TAG2", "This is random:" + Math.random()); // << stays, same as above
setContentView(R.layout.activity_main);
}
使用此混淆配置:
-assumenosideeffects class android.util.Log {
public static *** isLoggable(java.lang.String, int);
public static *** d(...);
public static *** v(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}
它将被混淆为一个可以反编译和反混淆为以下内容的状态:
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
double d = 1.0D + Math.random();
new StringBuilder("log_index=").append(d).toString();
new b();
a.a("no_log" + d);
new StringBuilder("log").append(d).toString();
a.a();
new StringBuilder("This is random:").append(Math.random()).toString();
setContentView(2130903064);
}