Затмение, Android, Scala сделано легко, но все еще не работает - PullRequest
38 голосов
/ 29 марта 2012

Я недавно пошел по пути программирования для Android с использованием Scala и Eclipse , который сокращает код и время компиляции без использования Proguard или Treeshake.

После этой статьи я должен бытьвозможность использовать последнюю сборку Eclipse (3.7), практически последнюю версию Scala (2.8.1), обновленную на эмуляторе версии 10, версию 2.8.3 в Eclipse с предоставленным плагином.

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

Я выполнил шаги, создал hello world, добавил природу scalaдобавил фиктивный класс scala, переместил компоновщик Scala перед установщиком пакетов Android, все прекрасно собирается, но когда я запускаю apk на эмуляторе из Eclipse, приложение вылетает, и я получаю следующую ошибку, которая выглядит как так же, как представлено здесь (в конце документа):

    03-29 10:29:38.505: E/AndroidRuntime(839): java.lang.NoClassDefFoundError: upg.TestSinceInstallation.ComputeSum

If Я удаляю ссылку scala в файле активности, она хорошо работает.

Вот файл TestSinceInstallation.java:

    package upg.TestSinceInstallation;

    import android.app.Activity;
    import android.os.Bundle;
    import upg.TestSinceInstallation.ComputeSum;

    public class TestSinceInstallationActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int a = 1;
            int b = 5;
            ComputeSum cs = new ComputeSum(a, b);
            if(cs.getResut() == 6) {
              setContentView(R.layout.main);
            }
        }
    }

, а вот файл ComputeSum.scala

    package upg.TestSinceInstallation

    class ComputeSum(a: Int, b: Int) {
      def getResut() : Int = a + b
    }

Как вы думаете, что я должен сделать, чтобы сделать эту работу?Я чувствую себя так близко к цели.

1 Ответ

56 голосов
/ 18 июня 2012

Вот решение: без проблем использовать Android с Eclipse 3.7 и Scala 3.0.0.

  • Установите Eclipse 3.7 (для меня 3.7.2) и Android SDK - плюс Java SDK 7v22 , если его еще нет в вашей системе. Внимание: специальный пакет Android ADT Bundle не позволяет устанавливать scala (по состоянию на июнь 2013 года, станция linux) .
  • Установите плагин Android ADT версии 22 для Eclipse, указав Eclipse на этот веб-сайт:

https://dl -ssl.google.com / Android / затмение /

http://download.scala -ide.org / SDK / е37 / scala210 / стабильный / сайт

https://androidproguardscala.s3.amazonaws.com/UpdateSiteForAndroidProguardScala

Теперь, чтобы создать проект Scala,

  1. Создайте проект Android как обычно
  2. Щелкните правой кнопкой мыши по проекту, Configure, Add Scala nature
  3. Щелкните правой кнопкой мыши по проекту, Add AndroidProguardScala nature

Готово.


Скалярный код Android

Теперь хорошие вещи случаются. Во-первых, вы можете скальпировать любое действие и получить доступ к уникальным функциям scala, таким как:

  • Ленивые вычисления для определения представлений внутри тела класса
  • Неявные функции преобразования для настройки взаимодействия представления с вашим кодом
  • Нет точек с запятой и весь синтаксический сахар скалы.
  • Использование действующих лиц для действий , чтобы отличить поток пользовательского интерфейса от потока обработки.

Вот пример некоторых из них.

package com.example.testing;
import android.app.Activity
import android.os.Bundle
import scala.collection.mutable.Map
import android.view.View
import android.widget.SeekBar
import android.widget.ImageButton
import android.graphics.drawable.Drawable
import android.widget.TextView

trait ActivityUtil extends Activity {
  implicit def func2OnClickListener(func: (View) => Unit):View.OnClickListener = {
    new View.OnClickListener() { override def onClick(v: View) = func(v) }
  }
  implicit def func2OnClickListener(code: () => Unit):View.OnClickListener = {
    new View.OnClickListener() { override def onClick(v: View) = code() }
  }
  private var customOnPause: () => Unit = null
  override def onPause() = {
    super.onPause()
    if(customOnPause != null) customOnPause()
  }
  def onPause(f: =>Unit) = {
    customOnPause = {() => f}
  }
  private var customOnCreate: Bundle => Unit = null
  override def onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    if(customOnCreate != null) customOnCreate(savedInstanceState)
  }
  def onCreate(f: Bundle => Unit) = {
    customOnCreate = f
  }
  // Keep references alive when fetched for the first time.
  private implicit val vMap = Map[Int, View]()
  private implicit val ibMap = Map[Int, ImageButton]()
  private implicit val sbMap = Map[Int, SeekBar]()
  private implicit val tvMap = Map[Int, TextView]()
  private implicit val dMap = Map[Int, Drawable]()

  def findView[A <: View](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, findViewById(id).asInstanceOf[A])
  def findDrawable[A <: Drawable](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, getResources().getDrawable(id).asInstanceOf[A])

  implicit class RichView(b: View) { // Scala 2.10 !
    def onClicked(f: =>Unit) = b.setOnClickListener{ () => f }
  }
  // Implicit functions to operate directly on integers generated by android.
  implicit def findViewImageButton(id: Int): ImageButton = findView[ImageButton](id)
  implicit def findViewSeekBar(id: Int): SeekBar = findView[SeekBar](id)
  implicit def findViewTextView(id: Int): TextView = findView[TextView](id)
  implicit def findDrawable(id: Int): Drawable = findDrawable[Drawable](id)
  implicit def findRichView(id: Int): RichView = toRichView(findView[View](id))
}

Теперь, после всех предыдущих примеров, очень полезно написать краткие действия. Обратите внимание, как мы можем напрямую работать с идентификаторами, как если бы они были представлениями. Двусмысленность необходима как (view: TextView).requestFocus(), если методы могут быть выведены из различных структур.

// Now after all the boilerplate, it is very useful to write consise activities
class MyActivity extends Activity with ActivityUtil {
  import R.id._ // Contains R.id.button, R.id.button2, R.id.button3, R.id.mytextview
  lazy val my_button: ImageButton = button   //In reality, R.id.button
  lazy val his_button: ImageButton = button2

  onCreate { savedInstanceState =>       // The type is automatically inferred.
    setContentView(R.layout.main)
    my_button.setOnClickListener(myCustomReactClick _)
    his_button.setOnClickListener { () =>
       //.... Scala code called after clicking his_button
    }
    button3.onClicked {
      // Awesome way of setting a method. Thanks Scala.
    }
    mytextview.setText("My text")  // Whoaaa ! setText on an integer.
    (mytextview: TextView).requestFocus() // Disambiguation because the method is ambiguous
    // Note that because of the use of maps, the textview is not recomputed.
  }

  def myCustomReactClick(v: View) = {
    // .... Scala code called after clicking my_button
  }
  onPause{
    // ... Custom code for onPause
  }
}

Убедитесь, что имя файла scala совпадает с основным действием, содержащимся в нем, в этом случае оно должно быть MyActivity.scala.

Во-вторых, чтобы настроить проект scala как проект библиотеки, чтобы использовать его в качестве основы для приложений, имеющих различные ресурсы, следуйте обычному способу настройки проекта библиотеки . Щелкните правой кнопкой мыши по проекту scala, который вы хотите использовать в качестве проекта базовой библиотеки, Properties, Android и выберите isLibrary.
Чтобы создать производный проект с использованием этой библиотеки и для которого вы можете сгенерировать APK, создайте новый проект android, не добавляя при этом никакой природы scala или androidproguardscala, просто щелкните правой кнопкой мыши, Properties, Android и добавьте предыдущий проект scala как библиотека.

ОБНОВЛЕНИЕ В новой версии подключаемого модуля Android перейдите на Project Properties > Build Päth > Order and Export и отметьте Android Private Libraries. Это позволит экспортировать библиотеку scala как в проект библиотеки, так и в основной проект, даже если основной проект не назначен Scala.

ТЕСТИРОВАНИЕ Использование плагина Robolectric позволяет легко протестировать ваш проект Scala для Android. Просто следуйте инструкциям по созданию тестового проекта, добавьте в него природу Scala. Вы даже можете использовать новый отладчик scala, и, добавив библиотеку scalatest , вы сможете использовать средства сравнения и многие другие функции, которые есть в Scala.

...