proguardではまる

『初めてのAndroid』をScalaで写経中にproguardではまった話。

AndroidにはJavaScriptからアプリ側を触りにいけるという結構おそろしげな機能があって、『初めてのAndroid』の7.3のLocalBrowserにその例がある。
具体的にはWebViewにaddJavascriptInterfaceでオブジェクトとインターフェイス名を登録するとJavaScript側からそのインターフェイス名でアクセスできるというもの。

しかし実際にアプリを動かしてみるとアプリ側を叩きに来ている様子が全くない。
当初Scalaだとそういうコールバック的な処理にはコツがあるのではと考えてコップ本をひっくり返したり、生まれて初めてjavap使ったり、果てはあまり関係ないと思いつつサイ本にまで手を出してみたもののまったく成果なし。

途方に暮れていたとき、ふとそういえばproguard使っていたなと思い、試しにproguardの処理をandroid_rules.xmlから外してビルドしてみたところこれが見事にビンゴ。ちゃんと動いてくれた。

proguardのマニュアルにあたってみると、


Processing callback methods

If your application, applet, servlet, library, etc., contains callback methods, which are called from external code (native code, scripts,...),you'll want to preserve them, and probably their classes too. They are just entry points to your code, much like, say, the main method of an application.
If they aren't preserved by other -keep options, something like
the following option will keep the callback class and method:

-keep class mypackage.MyCallbackClass {
    void myCallbackMethod(java.lang.String);
}

This will preserve the given class and method from being removed or renamed.

だそうで、以下の修正をandroid_rules.xmlに加えて無事動作。

--- android_rules.xml.orig  2010-04-04 17:20:03.000000000 +0900
+++ android_rules.xml 2010-04-04 17:20:54.000000000 +0900
@@ -201,6 +201,7 @@
      </outjar>
          -libraryjars ${android-jar}
          -keep public class * extends android.app.Activity
+     -keep class org.example.localbrowser.LocalBrowser$AndroidBridge{ void callAndroid(java.lang.String);}
      </proguard>
  </target>


あと本題と全然関係ないけど

GOOGLE ANDROIDアプリケーション開発入門

GOOGLE ANDROIDアプリケーション開発入門

こいつが届いたので写経する本をこっちに変更。