Не могу найти метод в деятельности - PullRequest
3 голосов
/ 11 мая 2010

Я начинаю с Scala + Android (и использую плагин sbt android). Я пытаюсь связать действие кнопки с кнопкой без действия, реализующего View.OnClickListener.

Нажатие кнопки не выполняется во время выполнения, поскольку метод не найден. В документе, над которым я работаю, говорится, что мне нужно только объявить открытый метод void, использующий View для действия, и использовать имя этого метода в макете.

Что я сделал не так?

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))
}

Рез / макет / 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

UPDATE

Думая, что это могло быть ошибкой с плагином 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;
}

1 Ответ

6 голосов
/ 27 мая 2010

Плагин sbt android включает задачу ProGuard, которая удаляет весь неиспользуемый код. Очень классные вещи, которые помогают действительно уменьшить размер получаемого файла .apk, но, к сожалению, методы обратного вызова обычно не упоминаются в вашем коде, поэтому, по умолчанию, ProGuard выбросит их. Чтобы увидеть это сами, попробуйте использовать javap для класса MainActivity, но установите для classpath значение target / your_scala_version / classes.min.jar. Вы должны сказать proguard, что явно хранить. В настройках по умолчанию для плагина sbt android уже есть набор параметров -keep, но это относится к вашему проекту, поэтому вам придется отредактировать определение проекта в project / build / YourProjectName.scala. Посмотрите на код sbt android plugin и lok для определения proguardTask. Вам придется переопределить это и добавить дополнительные опции -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, я добавил опцию -printseeds и one -keep, чтобы сохранить метод Calculate () функции MainActivity. -printseeds хорош для отладки, так как он сообщает proguard выводить имена сохраненных классов и методов. ProGuard имеет огромный набор параметров конфигурации, и вам нужно будет следить за ними при создании проекта, так как есть много неоднозначных ситуаций, когда ProGuard не будет делать правильные вещи по умолчанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...