$30 off During Our Annual Pro Sale. View Details »

Groovy for Android

Groovy for Android

Groovy and SwissKnife for being more productive in your Android development

Guillaume Laforge

June 23, 2015
Tweet

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

  1. Groovy
    on Android
    Guillaume Laforge
    Project Ninja and Advocate — Restlet
    @glaforge

    View Slide

  2. Groovy
    on Android
    Guillaume Laforge
    Project Ninja and Advocate — Restlet
    @glaforge
    I’ll put the slides online on
    speakerdeck.com/glaforge

    View Slide

  3. We know
    about
    APIs!

    View Slide

  4. 3
    APISpark — Backend as a Service
    • API platform for managing,
    documenting, and hosting
    APIs
    • Data stores
    • built-in entity store
    • Parse, Firebase & SQL
    wrappers
    • Google Sheets
    wrapper!
    • File stores
    • built-in file store
    • Github file store
    • S3 file store

    View Slide

  5. 3
    APISpark — Backend as a Service
    • API platform for managing,
    documenting, and hosting
    APIs
    • Data stores
    • built-in entity store
    • Parse, Firebase & SQL
    wrappers
    • Google Sheets
    wrapper!
    • File stores
    • built-in file store
    • Github file store
    • S3 file store
    Well suited for your 

    Backend-as-a-Service needs!

    View Slide

  6. 4
    RetroFit
    @Grab('com.squareup.retrofit:retrofit:1.9.0')

    import retrofit.*

    import retrofit.http.*


    interface StarWarsService {

    @Headers(["Accept: application/json" , "User-Agent: Firefox"])

    @Get('/people/{id}')

    People retrieve(@Path('id') String id)

    }


    class People { String name }


    def restAdapter = new RestAdapter.Builder()

    .setEndpoint('https://starwars.apispark.net/v1/').build()


    def service = restAdapter.create(StarWarsService)

    assert service.retrieve('1').name == 'Luke Skywalker'

    View Slide

  7. 5
    APISpark — sign-up!

    View Slide

  8. 5
    APISpark — sign-up!
    Go to http://restlet.com

    View Slide

  9. View Slide

  10. Total
    Android
    Newbie!

    View Slide

  11. View Slide

  12. Android is in the
    Java stone age
    state

    View Slide

  13. Is there
    something we
    can do about it?

    View Slide

  14. Yes, Groovy!

    View Slide

  15. 8
    New York Times — Getting Groovy with Android

    View Slide

  16. 8
    New York Times — Getting Groovy with Android
    http://bit.ly/nyt-groovy

    View Slide

  17. 9
    New York Times — Getting Groovy with Android

    View Slide

  18. 9
    New York Times — Getting Groovy with Android
    New York Times recruits a
    Groovy / Android expert
    http://bit.ly/nyt-job

    View Slide

  19. 10
    What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    Func0 func = new Func0() {
    @Override
    public String call() {
    return "my content";
    }
    };
    Async.start(func);

    View Slide

  20. 11
    What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    Async.start { "my content" }

    View Slide

  21. 11
    What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    Async.start { "my content" }
    Good bye annonymous
    inner classes!

    View Slide

  22. 12
    What does NYT likes about Groovy on Android?
    • Groovy code more concise and more readable
    • but just as type-safe as needed!

    (with @TypeChecked)
    • but just as fast as needed!

    (with @CompileStatic)

    View Slide

  23. Brief overview of Groovy

    View Slide

  24. Brief overview of Groovy
    You probably know it
    through Gradle already!

    View Slide

  25. Open-source project
    (Apache 2 licensed)
    • http://groovy-lang.org/
    14
    Groovy — a multi-faceted language

    View Slide

  26. 15
    Groovy — a multi-faceted language
    Alternative language 

    for the JVM

    View Slide

  27. 15
    Groovy — a multi-faceted language
    Alternative language 

    for the JVM
    An alternative
    language for the
    JVM

    View Slide

  28. 15
    Groovy — a multi-faceted language
    Alternative language 

    for the JVM
    With 4.5 million
    downloads a year

    View Slide

  29. 16
    Groovy — a multi-faceted language
    Closely 

    resembles 

    Java

    View Slide

  30. 16
    Groovy — a multi-faceted language
    Closely 

    resembles 

    Java
    Java code is also
    valid Groovy code!

    View Slide

  31. 16
    Groovy — a multi-faceted language
    Closely 

    resembles 

    Java
    But Groovy
    goes beyond!

    View Slide

  32. 17
    Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors

    View Slide

  33. 17
    Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    Object-oriented

    View Slide

  34. 17
    Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    Dynamic

    View Slide

  35. 17
    Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    Functional

    View Slide

  36. 17
    Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors Statically type checked
    and / or compiled

    View Slide

  37. 18
    Groovy — a multi-faceted language
    Seamless Java integration

    & interoperability

    View Slide

  38. 18
    Groovy — a multi-faceted language
    Seamless Java integration

    & interoperability
    No bridge to cross
    between languages

    View Slide

  39. Let’s get started?

    View Slide

  40. 20
    With a Hello World?

    View Slide

  41. 21
    Android Studio

    View Slide

  42. 21
    Android Studio
    Click on « New Project »

    View Slide

  43. 22
    Android Studio

    View Slide

  44. 22
    Android Studio
    Fill in the project details

    View Slide

  45. 23
    Android Studio

    View Slide

  46. 23
    Android Studio
    Choose your target
    platform

    View Slide

  47. 24
    Android Studio

    View Slide

  48. 24
    Android Studio
    Create a 

    blank activity

    View Slide

  49. 25
    Android Studio

    View Slide

  50. 25
    Android Studio
    Details for your
    new activity

    View Slide

  51. 26
    Android Studio

    View Slide

  52. 26
    Android Studio
    Now let’s
    have fun!

    View Slide

  53. 27
    Modify your app’s Gradle build
    apply plugin: 'com.android.application'
    android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"
    defaultConfig {
    applicationId "com.appspot.glaforge.hellogroovyworld"
    minSdkVersion 15
    targetSdkVersion 20
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    runProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 

    'proguard-rules.pro'
    }
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    }

    View Slide

  54. 27
    Modify your app’s Gradle build
    apply plugin: 'com.android.application'
    android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"
    defaultConfig {
    applicationId "com.appspot.glaforge.hellogroovyworld"
    minSdkVersion 15
    targetSdkVersion 20
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    runProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 

    'proguard-rules.pro'
    }
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    }
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath ‘com.android.tools.build:gradle:1.2.3’
    classpath ‘org.codehaus.groovy:gradle-groovy-android-plugin:0.3.6’
    }
    }

    View Slide

  55. 27
    Modify your app’s Gradle build
    apply plugin: 'com.android.application'
    android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"
    defaultConfig {
    applicationId "com.appspot.glaforge.hellogroovyworld"
    minSdkVersion 15
    targetSdkVersion 20
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    runProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 

    'proguard-rules.pro'
    }
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    }
    apply plugin: ‘groovyx.grooid.groovy-android’
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath ‘com.android.tools.build:gradle:1.2.3’
    classpath ‘org.codehaus.groovy:gradle-groovy-android-plugin:0.3.6’
    }
    }

    View Slide

  56. 27
    Modify your app’s Gradle build
    apply plugin: 'com.android.application'
    android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"
    defaultConfig {
    applicationId "com.appspot.glaforge.hellogroovyworld"
    minSdkVersion 15
    targetSdkVersion 20
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    runProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 

    'proguard-rules.pro'
    }
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    }
    compile 'org.codehaus.groovy:groovy:2.4.3:grooid'
    apply plugin: ‘groovyx.grooid.groovy-android’
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath ‘com.android.tools.build:gradle:1.2.3’
    classpath ‘org.codehaus.groovy:gradle-groovy-android-plugin:0.3.6’
    }
    }

    View Slide

  57. 28
    Java to Groovy…
    • Rename your activity from .java to .groovy
    • Remove…
    • public and return keywords
    • some parentheses
    • use the property notation
    • getMenuInflater() becomes menuInflater
    • use interpolated strings
    • and more.

    View Slide

  58. 29
    Java to Groovy…
    import android.app.Activity
    import android.os.Bundle
    import android.view.*
    class HelloGroovyWorld extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate savedInstanceState
    setContentView R.layout.activity_hello_groovy_world
    }
    boolean onCreateOptionsMenu(Menu menu) {
    menuInflater.inflate R.menu.hello_groovy_world, menu
    true
    }
    boolean onOptionsItemSelected(MenuItem item) {
    int id = item.itemId
    if (id == R.id.action_settings) {
    return true
    }
    super.onOptionsItemSelected item
    }

    View Slide

  59. 30
    Run your Groovy-powered app in the emulator

    View Slide

  60. 31
    What about weight?
    On a demo application (conference agenda)
    Artifact Size
    Groovy JAR 4.5 MB
    Generated
    APK
    2 MB
    After
    ProGuard
    1 MB
    (8K methods)

    View Slide

  61. 32
    ProGuard rules
    -dontobfuscate
    -keep class org.codehaus.groovy.vmplugin.**
    -keep class org.codehaus.groovy.runtime.dgm*
    -keepclassmembers class org.codehaus.groovy.runtime.dgm* {
    *;
    }
    -keepclassmembers class ** implements
    org.codehaus.groovy.runtime.GeneratedClosure {
    *;
    }
    -dontwarn org.codehaus.groovy.**
    -dontwarn groovy**

    View Slide

  62. How does the Groovy
    support work?

    View Slide

  63. 34
    Standard process
    Compile-time
    Runtime

    View Slide

  64. 34
    Standard process
    Compile-time
    Runtime
    .class

    View Slide

  65. 34
    Standard process
    Compile-time
    Runtime
    .class .dex

    View Slide

  66. 34
    Standard process
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  67. 34
    Standard process
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  68. 35
    First attempt — DiscoBot
    Compile-time
    Runtime

    View Slide

  69. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .apk

    View Slide

  70. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .class
    .apk

    View Slide

  71. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .class .jar
    .apk

    View Slide

  72. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .class .jar .dex
    .apk

    View Slide

  73. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .class .jar .dex
    .apk

    View Slide

  74. 35
    First attempt — DiscoBot
    Compile-time
    Runtime
    .class .jar .dex
    .apk
    « Ruboto » approach,
    slow & inneficient

    View Slide

  75. 36
    New process — just like Java!
    Compile-time
    Runtime

    View Slide

  76. 36
    New process — just like Java!
    Compile-time
    Runtime
    .class

    View Slide

  77. 36
    New process — just like Java!
    Compile-time
    Runtime
    .class .dex

    View Slide

  78. 36
    New process — just like Java!
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  79. 36
    New process — just like Java!
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  80. 37
    Evaluating Groovy code at runtime
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  81. 37
    Evaluating Groovy code at runtime
    Compile-time
    Runtime
    .class .dex .apk
    .class

    View Slide

  82. 37
    Evaluating Groovy code at runtime
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar

    View Slide

  83. 37
    Evaluating Groovy code at runtime
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar .dex

    View Slide

  84. 37
    Evaluating Groovy code at runtime
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar .dex

    View Slide

  85. What does Groovy bring?

    View Slide

  86. 39
    What does Groovy bring?
    Optional

    View Slide

  87. 39
    What does Groovy bring?
    Optional
    Semi-colons

    View Slide

  88. 39
    What does Groovy bring?
    Optional
    Parentheses

    View Slide

  89. 39
    What does Groovy bring?
    Optional
    Typing

    View Slide

  90. 39
    What does Groovy bring?
    Optional
    return keyworkd

    View Slide

  91. 39
    What does Groovy bring?
    Optional
    public keyworkd

    View Slide

  92. 40
    From Java to Groovy…
    public class Greeter {
    private String owner;
    public String getOwner() {
    return owner;
    }
    public void setOwner(String owner) {
    this.owner = owner;
    }
    public String greet(String name) {
    return "Hello " + name + ", I am " + owner;
    }
    }
    Greeter greeter = new Greeter();
    greeter.setOwner("Guillaume");
    System.out.println(greeter.greet("Marion"));

    View Slide

  93. 40
    From Java to Groovy…
    public class Greeter {
    private String owner;
    public String getOwner() {
    return owner;
    }
    public void setOwner(String owner) {
    this.owner = owner;
    }
    public String greet(String name) {
    return "Hello " + name + ", I am " + owner;
    }
    }
    Greeter greeter = new Greeter();
    greeter.setOwner("Guillaume");
    System.out.println(greeter.greet("Marion"));
    class Greeter {
    String owner
    String greet(String name) {
    "Hello ${name}, I am ${owner}"
    }
    }
    def greeter = new Greeter(owner: "Guillaume")
    println greeter.greet("Marion")

    View Slide

  94. 41
    Special Groovy syntax sugar — special operators

    View Slide

  95. 41
    Special Groovy syntax sugar — special operators
    // Groovy truth
    // if (s != null && s.length() > 0) {...}
    if (s) { ... }
    // Elvis
    def name = person.name ?: "unknown"
    // save navigation
    order?.lineItem?.item?.name

    View Slide

  96. 41
    Special Groovy syntax sugar — special operators
    // Groovy truth
    // if (s != null && s.length() > 0) {...}
    if (s) { ... }
    // Elvis
    def name = person.name ?: "unknown"
    // save navigation
    order?.lineItem?.item?.name
    if (person.name != null &&
    person.name.length() > 0)

    View Slide

  97. 41
    Special Groovy syntax sugar — special operators
    // Groovy truth
    // if (s != null && s.length() > 0) {...}
    if (s) { ... }
    // Elvis
    def name = person.name ?: "unknown"
    // save navigation
    order?.lineItem?.item?.name
    Copied by Swift, C# 

    and CoffeeScript!

    View Slide

  98. 41
    Special Groovy syntax sugar — special operators
    // Groovy truth
    // if (s != null && s.length() > 0) {...}
    if (s) { ... }
    // Elvis
    def name = person.name ?: "unknown"
    // save navigation
    order?.lineItem?.item?.name Anything null in the
    chain? Null

    View Slide

  99. 41
    Special Groovy syntax sugar — special operators
    // Groovy truth
    // if (s != null && s.length() > 0) {...}
    if (s) { ... }
    // Elvis
    def name = person.name ?: "unknown"
    // save navigation
    order?.lineItem?.item?.name
    Better NPEs: Cannot get
    property name on null object

    View Slide

  100. 42
    Special Groovy syntax sugar — collections, regex, closures
    // lists
    def list = [1, 2, 3, 4, 5]
    // maps
    def map = [a: 1, b: 2, c: 3]
    // regular expressions
    def regex = ~/.*foo.*/
    // ranges
    def range = 128..255
    // closures
    def adder = { a, b -> a + b }

    View Slide

  101. Builders

    View Slide

  102. Builders With closures come
    Groovy « builders »!

    View Slide

  103. 44
    Groovy builders — XML, JSON… Android views & layouts!
    import groovy.json.*
    def json = new JsonBuilder()
    json.person {
    name 'Guillaume'
    age 37
    daughters 'Marion', 'Erine'
    address {
    street '1 Main St'
    zip 75001
    city 'Paris'
    }
    }

    View Slide

  104. 44
    Groovy builders — XML, JSON… Android views & layouts!
    import groovy.json.*
    def json = new JsonBuilder()
    json.person {
    name 'Guillaume'
    age 37
    daughters 'Marion', 'Erine'
    address {
    street '1 Main St'
    zip 75001
    city 'Paris'
    }
    }
    {
    "person": {
    "name": "Guillaume",
    "age": 37,
    "daughters": [
    "Marion",
    "Erine"
    ],
    "address": {
    "street": "1 Main St",
    "zip": 75001,
    "city": "Paris"
    }
    }
    }

    View Slide

  105. 45
    HTTP GET and JSON parsing in 4 lines
    import groovy.json.*
    def url = "https://api.github.com/repos" +

    "groovy/groovy-core/commits"
    def commits = new JsonSlurper().parseText(url.toURL().text)
    assert commits[0].commit.author.name == 'Cedric Champeau'

    View Slide

  106. 45
    HTTP GET and JSON parsing in 4 lines
    import groovy.json.*
    def url = "https://api.github.com/repos" +

    "groovy/groovy-core/commits"
    def commits = new JsonSlurper().parseText(url.toURL().text)
    assert commits[0].commit.author.name == 'Cedric Champeau'
    An HTTP GET
    in a one-liner!

    View Slide

  107. 45
    HTTP GET and JSON parsing in 4 lines
    import groovy.json.*
    def url = "https://api.github.com/repos" +

    "groovy/groovy-core/commits"
    def commits = new JsonSlurper().parseText(url.toURL().text)
    assert commits[0].commit.author.name == 'Cedric Champeau'
    No complex object graph marshalling!

    View Slide

  108. 46
    Nice assertion failures
    def (a, b, c) = [20, 30, 40]
    assert a * (b - 1) / 10 == 3 * c / 2 + 1

    View Slide

  109. 46
    Nice assertion failures
    def (a, b, c) = [20, 30, 40]
    assert a * (b - 1) / 10 == 3 * c / 2 + 1
    Assertion failed:
    assert a * (b - 1) / 10 == 3 * c / 2 + 1
    | | | | | | | | | |
    | 580| 29 58 false| | 60 61
    20 30 | 40
    120
    at script1.run(script1.groovy:3)

    View Slide

  110. Annotations

    View Slide

  111. Annotations
    Actually, code transformations
    triggered by annotations

    View Slide

  112. Annotations
    Actually, code transformations
    triggered by annotations
    No APT, sorry!

    View Slide

  113. 48
    Dagger 2 and Groovy
    • Groovy’s « joint compilation » palliates lack of APT
    • generates Java stubs with annotations
    • Java stubs processed by Dagger’s processor
    • http://bit.ly/dagger-groovy

    View Slide

  114. 49
    AST transformations — only a short list!
    Code generation
    • @ToString
    • @EqualsAndHashCode
    • @Canonical
    • @TupleConstructor
    • @InheritConstructors
    • @Category
    • @Lazy
    • @Sortable
    • @Builder
    Class design
    • @Delegate
    • @Immutable
    • @Memoized
    • @Singleton
    Compiler directives
    • @AnnotationCollector
    • @DelegatesTo
    • @TypeChecked
    • @CompileStatic
    • @TailRecursive

    View Slide

  115. 50
    AST transformations — @Immutable
    Implement immutability by the book
    • final class
    • tuple-style constructor
    • private final backing fields
    • defensive copying of collections
    • equals() and hashCode() methods
    • toString() method
    • ...

    View Slide

  116. 51
    AST transformations — @Immutable
    • A person class
    • a String name
    • an int age
    public final class Person {
    private final String name;
    private final int age;
    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    public String getName() {
    return name;
    }
    public int getAge() {
    return age;
    }
    public int hashCode() {
    return age + 31 * name.hashCode();
    }
    public boolean equals(Object other) {
    if (other == null) {
    return false;
    }
    if (this == other) {
    return true;
    }
    if (Person.class != other.getClass()) {
    return false;
    }
    Person otherPerson = (Person)other;
    if (!name.equals(otherPerson.getName()) {
    return false;
    }
    if (age != otherPerson.getAge()) {
    return false;
    }
    return true;
    }
    public String toString() {
    return "Person(" + name + ", " + age + ")";
    }
    }

    View Slide

  117. 52
    AST transformations — @Immutable
    • A person class
    • a String name
    • an int age import groovy.transform.*
    @Immutable
    class Person {
    String name
    int age
    }

    View Slide

  118. 53
    AST transformations

    View Slide

  119. 53
    AST transformations
    Groovy allows
    you to be lazy

    View Slide

  120. 53
    AST transformations
    The compiler will do
    the job for you

    View Slide

  121. 53
    AST transformations
    More concise, more
    readable code

    View Slide

  122. 53
    AST transformations
    Less stuff to maintain
    and worry about

    View Slide

  123. 54
    Traits
    • Like interfaces, but with method bodies
    – similar to Java 8 interface default methods
    • Elegant way to compose behavior
    – multiple inheritance without the « diamond » problem
    • Traits can also be stateful
    – traits can have properties like normal classes
    • Compatible with static typing and static compilation
    – class methods from traits also visible from Java classes
    • Also possible to implement traits at runtime

    View Slide

  124. 55
    Traits — example: melix / speakertime
    @CompileStatic
    class PresentationActivity extends Activity
    implements GoogleApiProvider {
    // ...
    protected void onCreate(Bundle savedState) {
    super.onCreate(savedState)
    // ...
    createGoogleApi()
    }
    protected void onStart() {
    super.onStart()
    connectGoogleApi()
    }
    protected void onStop() {
    super.onStop()
    disconnectGoogleApi()
    }
    // ...
    @CompileStatic
    trait GoogleApiProvider {
    GoogleApiClient googleApiClient
    void createGoogleApi() { /* ... */ }
    void connectGoogleApi() { /* ... */ }
    void disconnectGoogleApi() { /* ... */ }
    }

    View Slide

  125. 56
    Compare Java and Groovy

    View Slide

  126. 56
    Compare Java and Groovy

    View Slide

  127. 57
    Event listeners
    button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
    Log.i(tag, "clicked!")
    }
    })

    View Slide

  128. 57
    Event listeners
    button.onClickListener = {
    Log.i(tag, "clicked!")
    }
    button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
    Log.i(tag, "clicked!")
    }
    })

    View Slide

  129. 58
    Properties and equality
    if (intent.getAction().equals(Intent.ACTION_VIEW)) {
    Log.i(tag, "view");
    }

    View Slide

  130. 58
    Properties and equality
    if (intent.getAction().equals(Intent.ACTION_VIEW)) {
    Log.i(tag, "view");
    }
    if (intent.action == Intent.ACTION_VIEW) {
    Log.i(tag, "view")
    }

    View Slide

  131. 59
    Groovy Development Kit methods
    new Thread() {
    public void run() {
    // …
    }
    }.start();

    View Slide

  132. 59
    Groovy Development Kit methods
    new Thread() {
    public void run() {
    // …
    }
    }.start();
    Thread.start {
    // …
    }

    View Slide

  133. 60
    Groovy’s with {} method
    view = new TextView(context);
    view.setText(name);
    view.setTextSize(16f);
    view.setTextColor(Color.WHITE);

    View Slide

  134. 60
    Groovy’s with {} method
    view = new TextView(context);
    view.setText(name);
    view.setTextSize(16f);
    view.setTextColor(Color.WHITE);
    view = new TextView(context)
    view.with {
    text = name
    textSize = 16f
    textColor = Color.WHITE
    }

    View Slide

  135. 61
    Groovy Truth and GStrings
    EditText phone = (EditText)findViewById(R.id.phone);
    if ((phone.getText() != null) && !phone.getText().equals("")) {
    String phoneString = parsePhone(phone.getText().toString());
    Intent intent = new Intent(Intent.ACTION_CALL,
    Uri.parse("tel:" + phoneString));
    startActivity(intent);
    }

    View Slide

  136. 61
    Groovy Truth and GStrings
    EditText phone = (EditText)findViewById(R.id.phone);
    if ((phone.getText() != null) && !phone.getText().equals("")) {
    String phoneString = parsePhone(phone.getText().toString());
    Intent intent = new Intent(Intent.ACTION_CALL,
    Uri.parse("tel:" + phoneString));
    startActivity(intent);
    }
    EditText phone = findViewById(R.id.phone)
    if (phone.text) {
    def phoneString = parsePhone(phone.text)
    def intent = new Intent(Intent.ACTION_CALL,
    Uri.parse("tel:${phoneString}"))
    startActivity intent
    }

    View Slide

  137. 62
    Resource handling with closures and file methods
    File f = new File("/sdcard/dir/f.txt");
    if (f.exists() && f.canRead()) {
    FileInputStream fis = null;
    try {
    fis = new FileInputStream(rFile);
    byte[] bytes = new byte[fis.available()];
    while (fis.read(bytes) != -1) {}
    textView.setText(new String(bytes));
    } catch (IOException e) {
    // log and or handle
    } finally {
    if (fis != null) {
    try {
    fis.close();
    } catch (IOException e) {
    // swallow
    }
    }
    }
    }

    View Slide

  138. 62
    Resource handling with closures and file methods
    File f = new File("/sdcard/dir/f.txt");
    if (f.exists() && f.canRead()) {
    FileInputStream fis = null;
    try {
    fis = new FileInputStream(rFile);
    byte[] bytes = new byte[fis.available()];
    while (fis.read(bytes) != -1) {}
    textView.setText(new String(bytes));
    } catch (IOException e) {
    // log and or handle
    } finally {
    if (fis != null) {
    try {
    fis.close();
    } catch (IOException e) {
    // swallow
    }
    }
    }
    }
    def f = new File("/sdcard/dir/f.txt")
    if (f.exists() && f.canRead()) {
    f.withInputStream { fis ->
    def bytes = new byte[fis.available()]
    while (fis.read(bytes) != -1) {}
    textView.text = new String(bytes)
    }
    }

    View Slide

  139. 62
    Resource handling with closures and file methods
    File f = new File("/sdcard/dir/f.txt");
    if (f.exists() && f.canRead()) {
    FileInputStream fis = null;
    try {
    fis = new FileInputStream(rFile);
    byte[] bytes = new byte[fis.available()];
    while (fis.read(bytes) != -1) {}
    textView.setText(new String(bytes));
    } catch (IOException e) {
    // log and or handle
    } finally {
    if (fis != null) {
    try {
    fis.close();
    } catch (IOException e) {
    // swallow
    }
    }
    }
    }
    def f = new File("/sdcard/dir/f.txt")
    if (f.exists() && f.canRead()) {
    f.withInputStream { fis ->
    def bytes = new byte[fis.available()]
    while (fis.read(bytes) != -1) {}
    textView.text = new String(bytes)
    }
    }
    def f = new File("/sdcard/dir/f.txt")
    if (f.exists() && f.canRead()) {
    textView.text = f.text
    }

    View Slide

  140. What else?

    View Slide

  141. 64
    What else?
    • Projects with dedicated Groovy support
    • Swiss Knife
    • similar to Butter Knife & 

    Android Annotations
    • Grooid Playground
    • a builder for creating views

    in a hierarchical & visual manner

    View Slide

  142. 65
    Swiss Knife — Arasthel / SwissKnife

    View Slide

  143. 65
    Swiss Knife — Arasthel / SwissKnife
    Like Butter Knife &
    Android Annotations,
    but for Groovy

    View Slide

  144. 66
    Swiss Knife
    class MyActivity extends Activity {
    @OnClick(R.id.button)
    void onButtonClicked(Button button) {
    Toast.makeText(this, "Clicked", Toast.LENGTH_SHOT).show()
    }
    @OnBackground
    void doSomeProcessing(URL url) {
    // Contents will be executed on background
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate savedInstanceState
    setContentView R.layout.activity_main
    SwissKnife.inject this
    }
    }

    View Slide

  145. 66
    Swiss Knife
    class MyActivity extends Activity {
    @OnClick(R.id.button)
    void onButtonClicked(Button button) {
    Toast.makeText(this, "Clicked", Toast.LENGTH_SHOT).show()
    }
    @OnBackground
    void doSomeProcessing(URL url) {
    // Contents will be executed on background
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate savedInstanceState
    setContentView R.layout.activity_main
    SwissKnife.inject this
    }
    }
    View
    injection

    View Slide

  146. 66
    Swiss Knife
    class MyActivity extends Activity {
    @OnClick(R.id.button)
    void onButtonClicked(Button button) {
    Toast.makeText(this, "Clicked", Toast.LENGTH_SHOT).show()
    }
    @OnBackground
    void doSomeProcessing(URL url) {
    // Contents will be executed on background
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate savedInstanceState
    setContentView R.layout.activity_main
    SwissKnife.inject this
    }
    }
    Execute code outside
    the UI thread

    View Slide

  147. 66
    Swiss Knife
    class MyActivity extends Activity {
    @OnClick(R.id.button)
    void onButtonClicked(Button button) {
    Toast.makeText(this, "Clicked", Toast.LENGTH_SHOT).show()
    }
    @OnBackground
    void doSomeProcessing(URL url) {
    // Contents will be executed on background
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate savedInstanceState
    setContentView R.layout.activity_main
    SwissKnife.inject this
    }
    }
    Injection point of views
    and callbacks

    View Slide

  148. 67
    Swiss Knife — available annotations
    • @OnUIThread
    • @OnBackground
    • @OnClick
    • @OnLongClick
    • @OnItemClick
    • @OnItemLongClick
    • @SaveInstance
    • @Parcelable
    • @OnItemSelected
    • @OnChecked
    • @OnFocusChanged
    • @OnTouch
    • @OnPageChanged
    • @OnTextChanged
    • @OnEditorAction
    • @Extra
    • @Res

    View Slide

  149. 67
    Swiss Knife — available annotations
    • @OnUIThread
    • @OnBackground
    • @OnClick
    • @OnLongClick
    • @OnItemClick
    • @OnItemLongClick
    • @SaveInstance
    • @Parcelable
    • @OnItemSelected
    • @OnChecked
    • @OnFocusChanged
    • @OnTouch
    • @OnPageChanged
    • @OnTextChanged
    • @OnEditorAction
    • @Extra
    • @Res
    AST transformations

    View Slide

  150. 68
    Swiss Knife — extension methods
    • Thanks to Groovy’s method extension mechanism, 

    can enrich any type with your own methods
    • DSL methods for
    • views
    • context
    • fragment
    • event
    • bundle

    View Slide

  151. 68
    Swiss Knife — extension methods
    • Thanks to Groovy’s method extension mechanism, 

    can enrich any type with your own methods
    • DSL methods for
    • views
    • context
    • fragment
    • event
    • bundle
    Adds missing methods you
    wish the Android SDK had!

    View Slide

  152. 69
    Swiss Knife — extension methods

    View Slide

  153. 70
    Swiss Knife — extension methods

    View Slide

  154. 71
    Swiss Knife — extension methods

    View Slide

  155. 72
    Swiss Knife — extension methods

    View Slide

  156. 73
    Swiss Knife — extension methods

    View Slide

  157. 74
    Swiss Knife — extension methods

    View Slide

  158. 75
    Swiss Knife — extension methods

    View Slide

  159. 76
    Swiss Knife — extension methods

    View Slide

  160. 77
    Swiss Knife — extension methods

    View Slide

  161. 78
    Swiss Knife — extension methods

    View Slide

  162. 79
    Swiss Knife — setup
    dependencies {

    compile 'com.arasthel:swissknife:1.3.1'

    }

    View Slide

  163. 80
    Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    def layout = new RelativeLayout(this)
    layout.width = MATCH_PARENT
    layout.height = MATCH_PARENT
    layout.setPaddingRelative(64, 16, 64, 16)
    def textView = new TextView(this)
    textView.width = MATCH_PARENT
    textView.height = 20
    textView.setText R.string.hello_world
    layout.addView textView
    setContentView(layout)

    View Slide

  164. 80
    Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    def layout = new RelativeLayout(this)
    layout.width = MATCH_PARENT
    layout.height = MATCH_PARENT
    layout.setPaddingRelative(64, 16, 64, 16)
    def textView = new TextView(this)
    textView.width = MATCH_PARENT
    textView.height = 20
    textView.setText R.string.hello_world
    layout.addView textView
    setContentView(layout)
    Imperative

    View Slide

  165. 80
    Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    setContentView new AndroidBuilder().build(this) {
    relativeLayout(width: MATCH_PARENT, height: MATCH_PARENT,
    padding: [64.dip, 16.dip]) {
    textView(width: MATCH_PARENT, height: 20.dip,
    text: R.string.hello_world)
    }
    }
    Declarative,
    hierarchical,
    visual

    View Slide

  166. Want more?

    View Slide

  167. 82
    Programming your Android applications in Groovy
    • You can use Groovy to code Android apps!
    • use Groovy 2.4.3
    • prefer @CompileStatic
    • Two great posts to get started:
    • http://bit.ly/grooid-1
    • http://bit.ly/grooid-2
    • Gradle plugin support:
    • http://bit.ly/grooid-gradle

    View Slide

  168. 83
    Demo conference application

    View Slide

  169. 83
    Demo conference application
    Source code available:
    http://bit.ly/grooid-app

    View Slide

  170. 84
    Demo Google Wear + Phone application

    View Slide

  171. 84
    Demo Google Wear + Phone application
    Source code available:
    http://bit.ly/grooid-wear

    View Slide

  172. Summary

    View Slide

  173. 86
    Summary
    • Groovy can make your Android code…
    • more concise & readable
    • more maintainable
    • Without compromising
    • type safety
    • speed

    View Slide

  174. 87
    Feedback welcome

    View Slide

  175. 87
    Feedback welcome
    We need you
    to help figure out
    how Groovy
    can help you!

    View Slide

  176. 88
    And if you need a Backend-as-a-Service
    http://restlet.com

    View Slide

  177. Q & A

    View Slide

  178. View Slide

  179. 91
    Image credits
    • Stone age
    • http://3.bp.blogspot.com/-lMDyyBdibG4/UxDYLq5ItiI/AAAAAAAALf8/HPpKRixHOIE/s1600/Neanderthal+Man+1.jpg
    • Many thanks
    • http://www.trys.ie/wp-content/uploads/2013/06/many-thanks.jpg
    • Disclaimer
    • http://3.bp.blogspot.com/-RGnBpjXTCQA/Tj2h_JsLigI/AAAAAAAABbg/AB5ZZYzuE5w/s1600/disclaimer.jpg
    • Builders
    • http://detroittraining.com/wp-content/uploads/Construction-Women2.jpg
    • Newbie
    • http://contently.com/strategist/wp-content/uploads/2014/05/bigstock-Shocked-Computer-Nerd-1520709.jpg
    • Alternative rock band
    • http://upload.wikimedia.org/wikipedia/commons/b/b2/Arcade_Fire_live_20050315.ext.jpg
    • Twins
    • http://images.doctissimo.fr/1/jumeaux-jumelles/photo/hd/4424027442/137540656da/jumeaux-jumelles-jumeaux-jumelles-2-big.jpg
    • Annotations
    • http://3.bp.blogspot.com/-f94n9BHko_s/T35ELs7nYvI/AAAAAAAAAKg/qe06LRPH9U4/s1600/IMG_1721.JPG
    • Apple & Orange
    • http://cdni.wired.co.uk/1920x1280/a_c/comparison.jpg
    • We need you
    • http://media.moddb.com/images/mods/1/14/13849/WickedSunshine_UncleSam_Blank_800x1000.png

    View Slide

  180. 92
    Image credits
    • Macarons
    • http://www.thatfoodcray.com/wp-content/uploads/2013/12/that-food-cray-pierre-herme-paris-hong-kong-christmas-flavors-foie-gras-truffle-9.jpg?
    4dd047
    • Bridge
    • http://tenspeedhero.com/wp-content/uploads/Bridge_3.jpg
    • Swiss knife
    • http://www.formengifts.com/wp-content/uploads/2011/04/swiss-army-champ-multitool-knife2.jpg
    • Hello World
    • http://www.warrenpriestley.com/wp-content/uploads/2012/01/helloworld.gif
    • George Clooney
    • http://www.brewville.ca/wp-content/uploads/2014/05/nespresso-george-clooney.jpg
    • Weight scale
    • http://images.wisegeek.com/person-stands-on-scale.jpg
    • Nexus 5
    • http://fs01.androidpit.info/userfiles/2692059/image/Blog/nexus-5-weiss_628.jpg
    • File icon
    • https://dl.vecnet.org/assets/default.png
    • Gear
    • http://www.pvzgears.com/wp-content/uploads/2013/03/gears2a.png
    • Sugar
    • http://images.wisegeek.com/cane-sugar.jpg

    View Slide