3

私は Scala + Android (および sbt android プラグインを使用) から始めています。View.OnClickListener を実装するアクティビティを使用せずに、ボタン アクションをボタンに接続しようとしています。

メソッドが見つからないため、ボタンのクリックは実行時に失敗します。私が取り組んでいるドキュメントには、アクションで View を取得する public void メソッドを宣言するだけでよく、そのメソッド名をレイアウトで使用する必要があると書かれています。

私は何を間違えましたか?

MainActivity.scala

package net.badgerhunt.hwa

import android.app.Activity
import android.os.Bundle
import android.widget.Button
import android.view.View
import java.util.Date

class MainActivity extends Activity {
  override def onCreate(savedInstanceState: Bundle) = {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)
  }
  def calculate(button: View): Unit = println("calculating with %s ...".format(button))
}

res/レイアウト/main.xml

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/button"
    android:text=""
    android:onClick="calculate"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>

クリックの失敗

D/AndroidRuntime(  362): Shutting down VM
W/dalvikvm(  362): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
E/AndroidRuntime(  362): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime(  362): java.lang.IllegalStateException: Could not find a method calculate(View) in the activity
E/AndroidRuntime(  362):    at android.view.View$1.onClick(View.java:2020)
E/AndroidRuntime(  362):    at android.view.View.performClick(View.java:2364)
E/AndroidRuntime(  362):    at android.view.View.onTouchEvent(View.java:4179)
E/AndroidRuntime(  362):    at android.widget.TextView.onTouchEvent(TextView.java:6540)
E/AndroidRuntime(  362):    at android.view.View.dispatchTouchEvent(View.java:3709)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
E/AndroidRuntime(  362):    at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
E/AndroidRuntime(  362):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
E/AndroidRuntime(  362):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(  362):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(  362):    at android.app.ActivityThread.main(ActivityThread.java:4363)
E/AndroidRuntime(  362):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(  362):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(  362):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
E/AndroidRuntime(  362):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
E/AndroidRuntime(  362):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(  362): Caused by: java.lang.NoSuchMethodException: calculate
E/AndroidRuntime(  362):    at java.lang.ClassCache.findMethodByName(ClassCache.java:308)
E/AndroidRuntime(  362):    at java.lang.Class.getMethod(Class.java:1014)
E/AndroidRuntime(  362):    at android.view.View$1.onClick(View.java:2017)
E/AndroidRuntime(  362):    ... 20 more

アップデート

これは sbt android プラグインのエラーかもしれないと考えて、コンパイル後にメソッドが存在することを二重に確認しました。javap を使用して ...

Compiled from "MainActivity.scala"
public class net.badgerhunt.hwa.MainActivity extends android.app.Activity implements scala.ScalaObject{
    public net.badgerhunt.hwa.MainActivity();
    public void calculate(android.view.View);
    public void onCreate(android.os.Bundle);
    public int $tag()       throws java.rmi.RemoteException;
}
4

1 に答える 1

6

sbt android プラグインには、未使用のコードをすべて削除する ProGuard タスクが含まれています。非常に優れたもので、結果の .apk ファイルを本当にスリム化するのに役立ちますが、残念ながら、コールバック メソッドは通常、コードで参照されないため、デフォルトでは、ProGuard はそれらを破棄します。自分で確認するには、MainActivity クラスで javap を使用してみてください。ただし、クラスパスを target/your_scala_version/classes.min.jar に設定してください。プロガードに明示的に何を保持するかを伝える必要があります。sbt android プラグインのデフォルト セットアップには既に一連の -keep オプションがありますが、これはプロジェクトに固有であるため、project/build/YourProjectName.scala でプロジェクト定義を編集する必要があります。proguardTask の定義については、sbt android プラグインと lok のコードを参照してください。それをオーバーライドして、追加の -keep オプションを追加する必要があります。これは私がしたことです:

import sbt._
import java.io._
import proguard.{Configuration=>ProGuardConfiguration, ProGuard, ConfigurationParser}
import sbt._
import Process._

trait Defaults {
  def androidPlatformName = "android-1.6"
}
class TestAndro2(info: ProjectInfo) extends ParentProject(info) {
  override def shouldCheckOutputDirectories = false
  override def updateAction = task { None }

  lazy val main  = project(".", "testAndro2", new MainProject(_))

  class MainProject(info: ProjectInfo) extends AndroidProject(info) with Defaults {
    val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test"
    override def proguardTask = task { 
      val args = "-injars" ::  mainCompilePath.absolutePath+File.pathSeparator+
      scalaLibraryJar.getAbsolutePath+"(!META-INF/MANIFEST.MF,!library.properties)"+
      (if (!proguardInJars.getPaths.isEmpty) File.pathSeparator+proguardInJars.getPaths.map(_+"(!META-INF/MANIFEST.MF)").mkString(File.pathSeparator) else "") ::                             
        "-outjars" :: classesMinJarPath.absolutePath ::
        "-libraryjars" :: libraryJarPath.getPaths.mkString(File.pathSeparator) :: 
        "-dontwarn" :: "-dontoptimize" :: "-dontobfuscate" :: 
        "-dontwarn" :: "-dontoptimize" :: "-dontobfuscate" :: "-printseeds" ::
        """-keep public class com.test.android.MainActivity {
          public void calculate(android.view.View);
        }""" ::
        "-keep public class * extends android.app.Activity" ::
        "-keep public class * extends android.app.Service" ::
        "-keep public class * extends android.appwidget.AppWidgetProvider" ::
        "-keep public class * implements junit.framework.Test { public void test*(); }" :: proguardOption :: Nil

        val config = new ProGuardConfiguration
        new ConfigurationParser(args.toArray[String], info.projectPath.asFile).parse(config)    
        new ProGuard(config).execute
        None
      }
    }
  }

Essentially、MainActivity の calculate() メソッドを保持するために、-printseeds と 1 つの -keep オプションを追加しました。-printseeds は、保持されているクラスとメソッドの名前を出力するようにプロガードに指示するため、デバッグに適しています。ProGuard には構成オプションの膨大なセットがあり、ProGuard がデフォルトで正しいことをしないあいまいな状況がたくさんあるため、プロジェクトをビルドするときにそれらに注意する必要があります。

于 2010-05-27T08:30:21.530 に答える