2011-10-02 46 views
6

我已經在Android Marketplace上分發了一個應用程序。我從一小部分用戶(可能是2%)收到錯誤報告,他們在那裏得到NullPointerExceptions,而這在邏輯上並不合理。Android proguard混淆代碼導致NullPointerException,當它真的不應該

我從來沒有能夠自己複製這個。代碼相對簡單,是每個用戶必須遵循的通用代碼路徑。我實際上已經採取了可能創建NPE並將其包裝在try-catch塊中並引發自定義運行時異常的每一行代碼,但我仍然遇到NullPointerException錯誤未被捕獲。

在這一點上,我能想象到的唯一的事情就是與我的Proguard混淆有關的東西。如果您注意到奇怪的行爲,我已經看過其他一些文章談論取消超越選項,但據我所知,我沒有使用該選項。

有沒有人經歷過使用android和proguard的神祕NPEs。是否還有其他設置可以推薦用於撥出可能導致此問題的優化?

還有其他想法嗎?

僅供參考,這裏是得到NPE的是非模糊的功能:

public MainMenuScreen(final HauntedCarnival game) { 
    super(game); 

    game.startMusic("data/music/intro.mp3"); 

    stage = new Stage(Screen.SCREEN_WIDTH, Screen.SCREEN_HEIGHT,true); 
    stage.addActor(new Image("background", Assets.mainMenuBackground)); 
    Image title = new Image("title", Assets.mainMenuTitle); 
    title.x = 0; 
    title.y = 340; 
    resetEyeBlink(); 
    stage.addActor(title); 
    dispatcher.registerInputProcessor(stage); 
    settings = game.getSettings(); 

    eyeBlinkImage = new Image("eyeBlink", Assets.eyeBlink); 
    if (settings.getPlayers().isEmpty()) { 
     settings.addPlayer("Player One"); 
     settings.save(game); 
    } 
    setupContinue(); 


} 

所以,唯一的可能性,我可以看到的是遊戲,調度程序和設置。

遊戲通過此代碼在另一個類中設置。遊戲是其他類中的最終變量:

game.setScreen(new MainMenuScreen(game)); 

調度程序在調用超級以上時被設置。

getSettings()返回一個在應用程序開始時設置的設置對象,是私有的,永遠不會被取消設置。它也曾用過這種方法幾次。

有沒有自動裝箱原語。

這裏是ProGuard配置文件:

-optimizationpasses 5 
-dontusemixedcaseclassnames 
-dontskipnonpubliclibraryclasses 
-dontpreverify 
-verbose 
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 

-keepattributes Signature 

-keep public class com.alkilabs.hauntedcarnival.settings.Settings 
-keep public class com.alkilabs.hauntedcarnival.settings.Settings { 
    *; 
} 
-keep public class com.alkilabs.hauntedcarnival.settings.Player 
-keep public class com.alkilabs.hauntedcarnival.settings.Player { 
    *; 
} 
-keepnames public class com.alkilabs.hauntedcarnival.world.World 
-keepnames public class * extends com.alkilabs.hauntedcarnival.world.upgrades.Upgrade 
-keepnames public class * extends com.alkilabs.hauntedcarnival.world.achievments.Achievement 

-keepnames public class com.alkilabs.hauntedcarnival.world.monsters.MonsterType 
-keepclassmembers class * extends com.alkilabs.hauntedcarnival.world.monsters.Monster { 
    public <init>(com.alkilabs.hauntedcarnival.world.monsters.MonsterType, java.lang.Integer, com.alkilabs.hauntedcarnival.world.World); 
} 

-keepnames public class com.alkilabs.hauntedcarnival.world.items.ItemType 
-keepclassmembers class * extends com.alkilabs.hauntedcarnival.world.items.Item { 
    public <init>(com.alkilabs.hauntedcarnival.world.World, java.lang.Integer, java.lang.Integer); 
} 


-keep public class * extends android.app.Activity 
-keep public class * extends android.app.Application 
-keep public class * extends android.app.Service 
-keep public class * extends android.content.BroadcastReceiver 
-keep public class * extends android.content.ContentProvider 
-keep public class * extends android.app.backup.BackupAgentHelper 
-keep public class * extends android.preference.Preference 

-dontwarn com.badlogic.gdx.scenes.scene2d.ui.utils.DesktopClipboard 
-dontwarn com.badlogic.gdx.utils.JsonWriter 
-dontwarn com.badlogic.gdx.utils.XmlWriter 

-keepclasseswithmembernames class * { 
    native <methods>; 
} 

-keepclasseswithmembers class * { 
    public <init>(android.content.Context, android.util.AttributeSet); 
} 

-keepclasseswithmembers class * { 
    public <init>(android.content.Context, android.util.AttributeSet, int); 
} 

-keepclassmembers class * extends android.app.Activity { 
    public void *(android.view.View); 
} 

-keepclassmembers enum * { 
    public static **[] values(); 
    public static ** valueOf(java.lang.String); 
} 

-keep class * implements android.os.Parcelable { 
    public static final android.os.Parcelable$Creator *; 
} 
+0

您是否可以發佈導致您使用NPE和ProGuard設置的代碼? – Idolon

+0

我添加了更多關於特定代碼和我的proguard配置的詳細信息 – Paul

回答

2

您最好的選擇是使用的mapping.txt文件和回掃工具來查找錯誤的確切位置。 從那裏可以更容易理解,如果它確實是Proguard或其他您沒有想到的最終案例。

要做到這一點,你需要的堆棧跟蹤從開發者控制檯複製到另一個文件中,我們假設它被稱爲

C:\ trace.txt現在

,在您的項目,你會發現有4個文件的Proguard文件夾。 讓我們假設你的項目是

C:\項目

你需要做的就是運行回掃工具(使用更方便使用批處理文件)位於(修改到什麼你的Android SDK文件夾的位置):

C:\ Android的SDK-WINDOWS \工具\ proguard的\ BIN \ retrace.bat

進入該文件夾,然後運行:

回掃C:\項目\ proguard的\的mapping.txt C:\ trace.txt

從那裏,它會更容易弄清楚我們確切例外的行,並可能找到錯誤。

從我的經驗來看,唯一可能會搞砸的是第三方庫。對於我的所有項目來說,普通的Android代碼從未被模糊處理過。

+0

是的,我已經做到了。我知道確切的方法和調用該方法的整個堆棧跟蹤。我唯一不知道的是行號,但這是因爲這是一個沒有行號的產品發行版。另外,據我所知,Proguard在優化階段實際上會重寫你的一些代碼(這是我懷疑proguard的原因之一)。所以我被降級到可能產生NPE的每條線的舊時尚調試,但是,唉,沒有骰子。 – Paul

+0

所以我唯一給你的建議是把一個未混淆的版本放幾天,看看你是否得到了額外的錯誤報告......我從來沒有在我的代碼中遇到Proguard的錯誤(從最複雜的應用程序到最簡單的應用程序),我總是混淆我的項目。 – IncrediApp

+0

是啊,我很擔心它可能要走到那,可惜我只是不想讓我的代碼出到野外都可以看到。我已經看到了一些其他職位談論的問題與proguard的代碼在手機上:http://stackoverflow.com/questions/93290/best-java-obfuscation-application-for-size-reduction所以我不認爲這是史無前例的。我只是希望其他人也可能有類似的情況。 – Paul

1

對不起,我還未發表評論(我是新來的)。

這可能是我遇到過的另一個問題。在某些手機上,在某些時候出現了像JSon庫那樣缺少Android庫的問題。

我建議你仔細看看真正獲得NPE的手機 - 可能有一些相似之處。

在我的情況下,它是HTC Desire Z,它缺少JSon庫,所以每當JSon部件被調用時,應用程序強制關閉。這個問題後來由HTC修復了Desire Z的ROM。

8

好吧,我想我已經找到了問題/困惑的根源。

proguard所做的一件事是內聯的一些方法。正因爲如此,我的構造函數底部的setupContinue()函數的全部內容直接添加到我的構造函數的內容中。所以現在我有更多的代碼可供審查,我確實看到了NPE的更多可能性。我很確定我會回到我的問題的底部。

我想通過採用proguard生成的obfuscated.jar並通過反編譯器運行它來解決這個問題。這是一個有趣的練習,因爲你可以更深入地瞭解proguard的內部運作。我強烈建議那些希望更好地理解proguard對代碼的影響的人。

+0

查看處理後的代碼可能非常有用。您應該確保您使用的是最新版本的ProGuard(4.6或4.7測試版,目前)。值得注意的是,版本4.6包含與靜態初始化程序相關的修復程序,因爲方法被移動(內聯),所以未運行。不過,令人驚訝的是,問題並未一致發生。 –

+0

關於Proguard內聯代碼的部分是一個很大的幫助。現在我明白爲什麼我的用戶提交崩潰/堆棧跟蹤似乎以方法簽名而不是導致NPE的實際代碼行結束。這太糟糕了,它並沒有特別指出導致問題的線路;如果該方法包含大量代碼(例如在大海撈針中找到針),那真是太吸引人了。 –