Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Groovy on Android -- Groovy Grails eXchange 2014

Groovy on Android -- Groovy Grails eXchange 2014

For 10 years, Groovy has dramatically improved the productivity of Java developers on the desktop. With unique like closures, builders, AST transformations, traits, optional static compilation and many more, Groovy turned out to be a very competitive language on the JVM. Compared to other JVM languages, Groovy has the major advantage of being totally Java-friendly, both in terms of syntax and interpretability. But during those years, what happened on the mobile world? In particular, Android developers are used to develop applications in Java, so why Groovy, a JVM language, wouldn't be usable for Android development too? Can we ease the pain of Android developers too?

Guillaume Laforge

December 12, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Programming

Transcript

  1. Groovy
    on Android
    Guillaume Laforge
    Groovy project lead
    @glaforge

    View Slide

  2. View Slide

  3. Total
    Android
    Newbie!

    View Slide

  4. View Slide

  5. Android is in the
    Java stone age
    state

    View Slide

  6. Is there
    something we
    can do about it?

    View Slide

  7. Yes, Groovy!

    View Slide

  8. New York Times — Getting Groovy with Android
    4

    View Slide

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

    View Slide

  10. New York Times — Getting Groovy with Android
    5

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. 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)
    8

    View Slide

  16. @glaforge — Groovy on Android
    Brief overview of Groovy

    View Slide

  17. @glaforge — Groovy on Android
    Brief overview of Groovy
    You probably know it
    through Gradle already!

    View Slide

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

    • http://beta.groovy-lang.org/
    10

    View Slide

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

    • http://beta.groovy-lang.org/
    10
    New « shiny » website
    coming soon!

    View Slide

  20. Groovy — a multi-faceted language
    Alternative language 

    for the JVM
    11

    View Slide

  21. Groovy — a multi-faceted language
    Alternative language 

    for the JVM
    11
    An alternative
    language for the
    JVM

    View Slide

  22. Groovy — a multi-faceted language
    Alternative language 

    for the JVM
    11
    With 3 million
    downloads a year

    View Slide

  23. Groovy — a multi-faceted language
    Closely 

    resembles 

    Java
    12

    View Slide

  24. Groovy — a multi-faceted language
    Closely 

    resembles 

    Java
    12
    Java code is also
    valid Groovy code!

    View Slide

  25. Groovy — a multi-faceted language
    Closely 

    resembles 

    Java
    12
    But Groovy
    goes beyond!

    View Slide

  26. Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    13

    View Slide

  27. Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    13
    Object-oriented

    View Slide

  28. Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    13
    Dynamic

    View Slide

  29. Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    13
    Functional

    View Slide

  30. Groovy — a multi-faceted language
    Multiple 

    programming 

    flavors
    13
    Statically type checked
    and / or compiled

    View Slide

  31. Groovy — a multi-faceted language
    Seamless Java integration

    & interoperability
    14

    View Slide

  32. Groovy — a multi-faceted language
    Seamless Java integration

    & interoperability
    14
    No bridge to cross
    between languages

    View Slide

  33. @glaforge — Groovy on Android
    Let’s get started?

    View Slide

  34. With a Hello World?
    16

    View Slide

  35. Android Studio
    17

    View Slide

  36. Android Studio
    17
    Click on « New Project »

    View Slide

  37. Android Studio
    18

    View Slide

  38. Android Studio
    18
    Fill in the project details

    View Slide

  39. Android Studio
    19

    View Slide

  40. Android Studio
    19
    Choose your target
    platform

    View Slide

  41. Android Studio
    20

    View Slide

  42. Android Studio
    20
    Create a 

    blank activity

    View Slide

  43. Android Studio
    21

    View Slide

  44. Android Studio
    21
    Details for your
    new activity

    View Slide

  45. Android Studio
    22

    View Slide

  46. Android Studio
    22
    Now let’s
    have fun!

    View Slide

  47. Modify your app’s Gradle build
    23
    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

  48. Modify your app’s Gradle build
    23
    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:0.12.2'
    classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.0'
    }
    }

    View Slide

  49. Modify your app’s Gradle build
    23
    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: 'me.champeau.gradle.groovy-android'
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:0.12.2'
    classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.0'
    }
    }

    View Slide

  50. Modify your app’s Gradle build
    23
    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.0-beta-3:grooid'
    apply plugin: 'me.champeau.gradle.groovy-android'
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:0.12.2'
    classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.0'
    }
    }

    View Slide

  51. 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.
    24

    View Slide

  52. Java to Groovy…
    25
    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

  53. Run your Groovy-powered app in the emulator
    26

    View Slide

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

    View Slide

  55. ProGuard rules
    28
    -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

  56. @glaforge — Groovy on Android
    How does the Groovy support work?

    View Slide

  57. Standard process
    30
    Compile-time
    Runtime

    View Slide

  58. Standard process
    30
    Compile-time
    Runtime
    .class

    View Slide

  59. Standard process
    30
    Compile-time
    Runtime
    .class .dex

    View Slide

  60. Standard process
    30
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  61. Standard process
    30
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  62. First attempt — DiscoBot
    31
    Compile-time
    Runtime

    View Slide

  63. First attempt — DiscoBot
    31
    Compile-time
    Runtime
    .apk

    View Slide

  64. First attempt — DiscoBot
    31
    Compile-time
    Runtime
    .class
    .apk

    View Slide

  65. First attempt — DiscoBot
    31
    Compile-time
    Runtime
    .class .jar
    .apk

    View Slide

  66. First attempt — DiscoBot
    31
    Compile-time
    Runtime
    .class .jar .dex
    .apk

    View Slide

  67. First attempt — DiscoBot
    31
    Compile-time
    Runtime
    .class .jar .dex
    .apk

    View Slide

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

    View Slide

  69. New process — just like Java!
    32
    Compile-time
    Runtime

    View Slide

  70. New process — just like Java!
    32
    Compile-time
    Runtime
    .class

    View Slide

  71. New process — just like Java!
    32
    Compile-time
    Runtime
    .class .dex

    View Slide

  72. New process — just like Java!
    32
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  73. New process — just like Java!
    32
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  74. Evaluating Groovy code at runtime
    33
    Compile-time
    Runtime
    .class .dex .apk

    View Slide

  75. Evaluating Groovy code at runtime
    33
    Compile-time
    Runtime
    .class .dex .apk
    .class

    View Slide

  76. Evaluating Groovy code at runtime
    33
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar

    View Slide

  77. Evaluating Groovy code at runtime
    33
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar .dex

    View Slide

  78. Evaluating Groovy code at runtime
    33
    Compile-time
    Runtime
    .class .dex .apk
    .class .jar .dex

    View Slide

  79. @glaforge — Groovy on Android
    What does Groovy bring?

    View Slide

  80. What does Groovy bring?
    35
    Optional

    View Slide

  81. What does Groovy bring?
    35
    Optional
    Semi-colons

    View Slide

  82. What does Groovy bring?
    35
    Optional
    Parentheses

    View Slide

  83. What does Groovy bring?
    35
    Optional
    Typing

    View Slide

  84. What does Groovy bring?
    35
    Optional
    return keyworkd

    View Slide

  85. What does Groovy bring?
    35
    Optional
    public keyworkd

    View Slide

  86. From Java to Groovy…
    36
    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

  87. From Java to Groovy…
    36
    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

  88. Special Groovy syntax sugar — special operators
    37

    View Slide

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

    View Slide

  90. Special Groovy syntax sugar — special operators
    37
    // 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

  91. Special Groovy syntax sugar — special operators
    37
    // 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

  92. Special Groovy syntax sugar — special operators
    37
    // 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

  93. Special Groovy syntax sugar — special operators
    37
    // 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

  94. Special Groovy syntax sugar — collections, regex, closures
    38
    // 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

  95. Builders

    View Slide

  96. Builders With closures come
    Groovy « builders »!

    View Slide

  97. Groovy builders — XML, JSON… Android views & layouts!
    40
    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

  98. Groovy builders — XML, JSON… Android views & layouts!
    40
    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

  99. HTTP GET and JSON parsing in 4 lines
    41
    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

  100. HTTP GET and JSON parsing in 4 lines
    41
    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

  101. HTTP GET and JSON parsing in 4 lines
    41
    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

  102. Annotations

    View Slide

  103. Annotations
    Actually, code transformations
    triggered by annotations

    View Slide

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

    View Slide

  105. 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
    43

    View Slide

  106. 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

    • ...
    44

    View Slide

  107. AST transformations — @Immutable
    • A person class

    • a String name

    • an int age
    45
    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

  108. AST transformations — @Immutable
    • A person class

    • a String name

    • an int age
    46
    import groovy.transform.*
    @Immutable
    class Person {
    String name
    int age
    }

    View Slide

  109. AST transformations
    47

    View Slide

  110. AST transformations
    47
    Groovy allows
    you to be lazy

    View Slide

  111. AST transformations
    47
    The compiler will do
    the job for you

    View Slide

  112. AST transformations
    47
    More concise, more
    readable code

    View Slide

  113. AST transformations
    47
    Less stuff to maintain
    and worry about

    View Slide

  114. 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 48

    View Slide

  115. Traits — example: melix / speakertime
    49
    @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

  116. Compare Java and Groovy
    50

    View Slide

  117. Compare Java and Groovy
    50

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  125. Groovy’s with {} method
    54
    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

  126. Groovy Truth and GStrings
    55
    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

  127. Groovy Truth and GStrings
    55
    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

  128. Resource handling with closures and file methods
    56
    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

  129. Resource handling with closures and file methods
    56
    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

  130. Resource handling with closures and file methods
    56
    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

  131. @glaforge — Groovy on Android
    What else?

    View Slide

  132. 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
    58

    View Slide

  133. Swiss Knife — Arasthel / SwissKnife
    59

    View Slide

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

    View Slide

  135. Swiss Knife
    60
    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

  136. Swiss Knife
    60
    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

  137. Swiss Knife
    60
    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

  138. Swiss Knife
    60
    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

  139. Swiss Knife — available annotations
    • @OnUIThread

    • @OnBackground

    • @OnClick

    • @OnLongClick

    • @OnItemClick

    • @OnItemLongClick

    • @OnItemSelected

    • @OnChecked

    • @OnFocusChanged

    • @OnTouch

    • @OnPageChanged

    • @OnTextChanged

    • @OnEditorAction
    61

    View Slide

  140. Swiss Knife — available annotations
    • @OnUIThread

    • @OnBackground

    • @OnClick

    • @OnLongClick

    • @OnItemClick

    • @OnItemLongClick

    • @OnItemSelected

    • @OnChecked

    • @OnFocusChanged

    • @OnTouch

    • @OnPageChanged

    • @OnTextChanged

    • @OnEditorAction
    61
    AST transformations

    View Slide

  141. Swiss Knife — setup
    62
    dependencies {

    compile 'com.arasthel:swissknife:1.0.4'

    }

    View Slide

  142. Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    63
    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

  143. Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    63
    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

  144. Grooid Playground — karfunkel / grooid-playground
    • When you need to build dynamic views (ie. not XML-driven)
    63
    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

  145. @glaforge — Groovy on Android
    Want more?

    View Slide

  146. Programming your Android applications in Groovy
    • You can use Groovy to code Android apps!

    • use Groovy 2.4.0-beta-1+ (latest: beta-4)

    • 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
    65

    View Slide

  147. Demo conference application
    66

    View Slide

  148. Demo conference application
    66
    Source code available:
    http://bit.ly/grooid-app

    View Slide

  149. Demo Google Wear + Phone application
    67

    View Slide

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

    View Slide

  151. @glaforge — Groovy on Android
    Summary

    View Slide

  152. Summary
    • Groovy can make your Android code…

    • more concise & readable
    • more maintainable
    • Without compromising

    • type safety
    • speed
    69

    View Slide

  153. Feedback welcome
    70

    View Slide

  154. Feedback welcome
    70
    We need you
    to help figure out
    how Groovy
    can help you!

    View Slide

  155. @glaforge — Groovy on Android
    Q & A

    View Slide

  156. View Slide

  157. 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
    73

    View Slide

  158. 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
    • London
    • http://www.100percentoptical.com/images/2014/10/london.jpg 74

    View Slide