Статьи

Scala на Android и прочее: извлеченные уроки

Я играю в ролевые игры с одиннадцати лет, и я и моя команда все еще играем один или два раза в год. Недавно они решили снова сыграть в Earthdawn, в игру, в которую мы не играем уже более 15 лет! Это вызвало мое желание создать приложение, чтобы бросать все эти странные кубики. И чтобы объединить полезное с приятным, я решил использовать технологии, с которыми я не очень знаком: язык Scala, платформа Android и система сборки Gradle.

Первым шагом было создание простого и универсального API прокрутки в Scala, и это было предметом одной из моих предыдущих статей . Вторым шагом было создание этого API для создания чего-то более специфичного для Earthdawn и разработки GUI. Вот описание моих размышлений в этом развитии.

Вот общий обзор компонентов:

Обзор компонентов

Переработка основ

После долгих внутренних дебатов я, наконец, изменил тип возвращаемого значения rollметода с (Rollable[T], T)простого Tследования за соответствующим комментарием на reddit. Я пришел к выводу, что звонящий должен сам взять кристалл и вернуть его, если захочет. Это то, что я сделал в доменном слое Earthdawn.

Scala характеристики

Использование Scala означало, что я также погрузился в среду Scala Specs 2. Specs 2 предлагает управляемый поведением способ написания тестов, а также интеграцию с JUnit через бегунов и Mockito через черты. Тестовые экземпляры могут быть инициализированы отдельно в отдельном классе, изолированном от всех остальных.

Моя самая большая проблема состояла в том, чтобы настроить плагин Maven Surefire для выполнения тестов Specs 2:

<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.17</version>
    <configuration>
        <argLine>-Dspecs2.console</argLine>
            <includes>
                <include>**/*Spec.java</include>
            </includes>
    </configuration>
</plugin>

Scala + Android = Scaloid

Though perhaps disappointing at first, wanting to run Scala on Android is feasible enough. Normal Java is compiled to bytecode and then converted to Dalvik-compatible Dex files. Since Scala is also compiled to bytecode, the same process can also be applied. Not only can Scala be easily be ported to Android, some frameworks to do that are available online: the one which seemed the most mature was Scaloid.

Scaloid most important feature is to eschew traditional declarative XML-based layout in favor of a Scala-based Domain Specific Language with the help of implicit:

val layout = new SLinearLayout {
  SButton("Click").<<.Weight(1.0f).>>
}

Scaloid also offers:

  • Lifecycle management
  • Implicit conversions
  • Trait-based hierarchy
  • Improved getters and setters
  • etc.

If you want to do Scala on Android, this is the project you’ve to look at!

Some more bitching about Gradle

I’m not known to be a big fan of Gradle – to say the least. The bigest reason however, is not because I think Gradle is bad but because using a tool based on its hype level is the worst reason I can think of.

I used Gradle for the API project, and I must admit it it is more concise than Maven. For example, instead of adding the whole maven-scala-plugin XML snippet, it’s enough to tell Gradle to use it with:

apply plugin: 'scala'

However the biggest advantage is that Gradle keeps the state of the project, so that unnecessary tasks are not executed. For example, if code didn’t change, compilation will not be executed.

Now to the things that are – to put in politically correct way, less than optimal:

  • First, interestingly enough, Gradle does not output test results in the console. For a Maven user, this is somewhat unsettling. But even without this flaw, I’m afraid any potential user is interested in the test ouput. Yet, this can be remedied with some Groovy code:
    test {
        onOutput { descriptor, event ->
            logger.lifecycle("Test: " + descriptor + ": " + event.message )
        }
    }
  • Then, as I wanted to install my the resulting package into my local Maven repository, I had to add the Maven plugin: easy enough… This also required Maven coordinates, quite expected. But why am I allowed to install without executing test phases??? This is not only disturbing, but I cannot accept any rationale to allow installing without testing.
  • Neither of the previous points proved to be a show stopper, however. For the Scala-Android project, you might think I just needed to apply both scala and android plugins and be done with that. Well, this is wrong! It seems that despite Android being showcased as the use-case for Gradle, scala and android plugins are not compatible. This was the end of my Gradle adventure, and probably for the foreseeable future.

Testing with ease and Genymotion

The build process i.e. transforming every class file into Dex, packaging them into an .apk and signing it takes ages. It would be even worse if using the emulator from Google Android’s SDK. But rejoice, Genymotion is an advanced Android emulator that not only very fast but easy as pie to use.

Instead of doing an adb install, installing an apk on Genymotion can be achieved by just drag’n’dropping it on the emulated device. Even better, it doesn’t require first uninstalling the old version and it launches the application directly. Easy as pie I told you!

Conclusion

I have now a working Android application, complete with tests and repeatable build. It’s not much, but it gets the job done and it taught me some new stuff I didn’t know previously. I can only encourage you to do the same: pick a small application and develop it with languages/tools/platforms that you don’t use in your day-to-day job. In the end, you will have learned stuff and have a working application. Doesn’t it make you feel warm inside?