2015/01/27

ActionBarActivity#setSupportActionBar() で NoClassDefFoundError が発生する端末がある

私が Google Play で公開しているアプリのクラッシュ報告に、以下のような Stack Trace が送られてきました。
java.lang.NoClassDefFoundError: android.support.v7.internal.view.menu.MenuBuilder
at android.support.v7.widget.ActionMenuView.getMenu(ProGuard:620)
at android.support.v7.widget.Toolbar.ensureMenu(ProGuard:825)
at android.support.v7.widget.Toolbar.getMenu(ProGuard:817)
at android.support.v7.internal.app.ToolbarActionBar.getMenu(ProGuard:554)
at android.support.v7.internal.app.ToolbarActionBar.setListMenuPresenter(ProGuard:558)
at android.support.v7.app.ActionBarActivityDelegateBase.setSupportActionBar(ProGuard:178)
at android.support.v7.app.ActionBarActivity.setSupportActionBar(ProGuard:92)
at com.kokufu.android.apps.divelogbook.MainActivity.onCreate(ProGuard:84)
at android.app.Activity.performCreate(Activity.java:5122)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1150)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2315)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2403)
at android.app.ActivityThread.access$600(ActivityThread.java:165)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1373)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5391)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)

原因

調べてみると、Samsung やその他一部の端末で発生するバグらしい。

対策

これ、appcompat-v7 のバグではなく、OS のバグだと思われます。 OS に古い appcompat-v7 が組み込まれているのが問題ではないかと。
つまり、アプリ側ではどうしようもない。 …と諦めかけてたのですが、ProGuard を使って回避するナイスなアイデアが!

難読化をしない場合、以下の一行を ProGuard の設定ファイルに記述すればオッケーです。
-keep class !android.support.v7.internal.view.menu.**,** {*;}

他にも難読化したい場合には、以下の方が無難でしょう。
-keep class !android.support.v7.internal.view.menu.**,android.support.v7.** { *; }
-keep interface !android.support.v7.internal.view.menu.**,android.support.v7.** { *; }

出力された mapping ファイルを確認し、MenuBuilder の名前が変わっていることを確認しておきます。

build/outputs/mapping/release/mapping.txt
android.support.v7.internal.view.menu.MenuBuilder -> android.support.v7.internal.view.menu.i:

私の手元には、この問題が起こる端末が無いため、動作確認出来ていません。
名前変えただけで、class path の問題を回避できるんだか、ちょっと不安です。
もし、確認された方がいらっしゃいましたら、是非教えてください。

0 件のコメント: