2011-08-17 207 views
20

可能重複的字符串:
Removing unused strings during ProGuard optimisation使用ProGuard刪除記錄不會被刪除記錄

我有幾十個記錄語句的Android應用程序。我寧願他們將不會出現在發行版本,所以我用Proguard的像這樣的東西在proguard.cfg文件:

-assumenosideeffects class android.util.Log { 
    public static *** d(...); 
} 

但問題是,有很多的Log.d("something is " + something),雖然Log.d()語句正在從字節碼中刪除,字符串仍然存在

所以,以下this答案,我創建了一個簡單的包裝類,沿着線的東西:

public class MyLogger { 
    public static void d(Object... msgs) { 
     StringBuilder log = new StringBuilder(); 
     for(Object msg : msgs) { 
      log.append(msg.toString()); 
     } 
     Log.d(TAG, log.toString()); 
    } 
} 

然後我編輯proguard.cfg

-assumenosideeffects class my.package.MyLogger { 
    public static *** d(...); 
} 

但字符串仍然被找到在生成的字節碼中!

除此之外,我使用Android SDK提供的標準proguard.cfg。難道我做錯了什麼?


編輯:檢查生成的字節碼之後,我看到了弦在那裏,但他們沒有被追加另一個人,因爲我以爲。他們正在存儲在一個陣列。爲什麼?將它們作爲可變參數傳遞給我的方法。它看起來像ProGuard的不喜歡,所以我修改了我的記錄器類是這樣的:

public static void d(Object a) { 
    log(a); 
} 

public static void d(Object a, Object b) { 
    log(a, b); 
} 

(... I had to put like seven d() methods ...) 

private static void log(Object... msgs) { 
    (same as before) 
} 

這是醜陋的,但現在的字符串是無處字節碼。

這是ProGuard的某種bug /限制嗎?還是隻是我不理解它是如何工作的?

+0

你可能想避免proguard黑客,只是使用一個常量:http://stackoverflow.com/questions/4199563/android-util-log-when-publishing-what-c​​an-i-do-not-do如果然後你使用一個常量proguard會在它==假時刪除代碼行。 – Blundell

+2

的確,這是一個黑客攻擊,也是一個醜陋的攻擊,但我認爲這比將if(DEBUG)放在任何地方都好。生成的代碼更清晰,結果在導出的應用程序中是相同的。另外,出於某種原因,如果調試常量是錯誤的,Eclipse會對死代碼抱怨,我不喜歡這樣。感謝您的想法,但:) –

+0

請注意,如果您傳遞基本數據類型,可能仍然有一些死代碼。 Proguard不會將基本數據類型移除到相應的「對象」轉換。例如。如果你傳遞一個'int',Proguard將會在'Integer.valueOf(someVar)'行中離開;' – crazymaik

回答

11

對於不同數量的參數,您使用不同方法的解決方案可能是最方便的。具有可變數量參數的單個方法會更好,但是當前版本的ProGuard不夠聰明,無法刪除所有未使用的代碼。

java編譯器將可變數量的參數編譯爲單個數組參數。在調用方面,這意味着創建一個合適大小的數組,填充元素並將其傳遞給方法。 ProGuard認爲該數組正在被創建,然後被用於存儲元素。它並沒有意識到(然而)它並沒有用於實際有用的操作,方法調用消失了。結果,數組的創建和初始化被保留。

如果您希望進行某些特定的優化,那麼檢查已處理的代碼是一種很好的做法。

+1

我剛開始使用職業守衛去掉日誌並面臨同樣的死代碼問題。我想知道爲什麼職業後衛無法在隨後的傳球中移除這些陣列。我見過IDEs指出一個局部變量從來沒有使用過。所以,這似乎是可能的。 –

4

Proguard的不工作我也一樣,我不喜歡創建一個圍繞日誌的包裝,所以我測試了以下解決方案:

工作液

final static boolean IsDebugging = false; 

並在代碼:

if(IsDebugging) 
    Log.i(TAG, "Debugging"); 

認爲你必須使用最終關鍵字,這樣ISDE在編譯時竊聽成爲真或假,並且日誌字符串不會出現在最終的字節碼中。

不工作(IsDebugMode)

public static boolean IsDebugMode(Context context) { 
    PackageManager pm = context.getPackageManager(); 
    PackageInfo pi; 
    try { 
     pi = pm.getPackageInfo(context.getPackageName(),0); 
    } catch (NameNotFoundException e) { 
     return false; 
    } 
    return (pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 
} 

,並在我的代碼:

boolean IsDebugging = Utils.IsDebugMode(this); 

    if(IsDebugging) 
     Log.i(TAG, "Debugging"); 

上述解決方案不打印logcat的任何日誌,但日誌字符串被編譯並存在最後的字節碼是壞的。

另一解決方案是:

不工作(BuildConfid.DEBUG)

if(BuildConfig.DEBUG) 
     Log.i(TAG, "Debugging"); 

這是相同IsDebugMode溶液。它不打印任何內容,但字符串在最終的字節碼中。又壞了!

+0

請參閱我的問題的第二個評論。 –