2015/01/27
ActionBarActivity#setSupportActionBar() で NoClassDefFoundError が発生する端末がある
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 の問題を回避できるんだか、ちょっと不安です。
もし、確認された方がいらっしゃいましたら、是非教えてください。