Groovy on Android -- DroidCon Paris 2014

Groovy on Android -- DroidCon Paris 2014

Groovy 2.4+ is providing support for developing Android applications in Groovy. In this presentation, we see how Groovy can help streamline the development of Android apps with the Groovy language.

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

September 22, 2014
Tweet

Transcript

  1. Groovy on Android Guillaume Laforge Groovy project lead (at Pivotal)

    @glaforge
  2. Groovy on Android Guillaume Laforge Groovy project lead (at Pivotal)

    @glaforge I’ll put the slides online on speakerdeck.com/glaforge
  3. None
  4. Total Android Newbie!

  5. None
  6. Android is in the Java stone age state

  7. Is there something we can do about it?

  8. Yes, Groovy!

  9. New York Times — Getting Groovy with Android 4

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

  11. New York Times — Getting Groovy with Android 5

  12. New York Times — Getting Groovy with Android 5 New

    York Times recruits a Groovy / Android expert http://bit.ly/nyt-job
  13. What does NYT likes about Groovy on Android? • No

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

    Java 8, no lambda on Android… 7 ! ! ! ! ! ! Async.start { "my content" }
  15. 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!
  16. 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
  17. @glaforge — Groovy on Android Brief overview of Groovy

  18. @glaforge — Groovy on Android Brief overview of Groovy You

    probably know it through Gradle already!
  19. Groovy — a multi-faceted language Open-source project (Apache 2 licensed)

    ! • http://groovy.codehaus.org/ • http://beta.groovy-lang.org/ 10
  20. 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!
  21. Groovy — a multi-faceted language Alternative language 
 for the

    JVM 11
  22. Groovy — a multi-faceted language Alternative language 
 for the

    JVM 11 An alternative language for the JVM
  23. Groovy — a multi-faceted language Alternative language 
 for the

    JVM 11 With 3 million downloads a year
  24. Groovy — a multi-faceted language Closely 
 resembles 
 Java

    12
  25. Groovy — a multi-faceted language Closely 
 resembles 
 Java

    12 Java code is also valid Groovy code!
  26. Groovy — a multi-faceted language Closely 
 resembles 
 Java

    12 But Groovy goes beyond!
  27. Groovy — a multi-faceted language Multiple 
 programming 
 flavors

    13
  28. Groovy — a multi-faceted language Multiple 
 programming 
 flavors

    13 Object-oriented
  29. Groovy — a multi-faceted language Multiple 
 programming 
 flavors

    13 Dynamic
  30. Groovy — a multi-faceted language Multiple 
 programming 
 flavors

    13 Functional
  31. Groovy — a multi-faceted language Multiple 
 programming 
 flavors

    13 Statically type checked and / or compiled
  32. Groovy — a multi-faceted language Seamless Java integration
 & interoperability

    14
  33. Groovy — a multi-faceted language Seamless Java integration
 & interoperability

    14 No bridge to cross between languages
  34. @glaforge — Groovy on Android Let’s get started?

  35. With a Hello World? 16

  36. Android Studio 17

  37. Android Studio 17 Click on « New Project »

  38. Android Studio 18

  39. Android Studio 18 Fill in the project details

  40. Android Studio 19

  41. Android Studio 19 Choose your target platform

  42. Android Studio 20

  43. Android Studio 20 Create a 
 blank activity

  44. Android Studio 21

  45. Android Studio 21 Details for your new activity

  46. Android Studio 22

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

  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']) }
  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']) } buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.2' classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.0' } }
  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']) } 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' } }
  51. 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' } }
  52. 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
  53. 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 }
  54. Run your Groovy-powered app in the emulator 26

  55. 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)
  56. 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**
  57. @glaforge — Groovy on Android How does the Groovy support

    work?
  58. Standard process 30 Compile-time Runtime

  59. Standard process 30 Compile-time Runtime .class

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

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

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

  63. First attempt — DiscoBot 31 Compile-time Runtime

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

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

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

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

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

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

    .apk « Ruboto » approach, slow & inneficient
  70. New process — just like Java! 32 Compile-time Runtime

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

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

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

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

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

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

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

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

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

    .apk .class .jar .dex
  80. @glaforge — Groovy on Android What does Groovy bring?

  81. What does Groovy bring? 35 Optional

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

  83. What does Groovy bring? 35 Optional Parentheses

  84. What does Groovy bring? 35 Optional Typing

  85. What does Groovy bring? 35 Optional return keyworkd

  86. What does Groovy bring? 35 Optional public keyworkd

  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"));
  88. 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")
  89. Special Groovy syntax sugar — special operators 37

  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
  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 if (person.name != null && person.name.length() > 0)
  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 Copied by Swift, C# 
 and CoffeeScript!
  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 Anything null in the chain? Null
  94. 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
  95. 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 }
  96. Builders

  97. Builders With closures come Groovy « builders »!

  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' } }
  99. 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" } } }
  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'
  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' An HTTP GET in a one-liner!
  102. 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!
  103. Annotations

  104. Annotations Actually, code transformations triggered by annotations

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

  106. 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
  107. 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
  108. 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 + ")"; } }
  109. AST transformations — @Immutable • A person class • a

    String name • an int age 46 import groovy.transform.* ! @Immutable class Person { String name int age }
  110. AST transformations 47

  111. AST transformations 47 Groovy allows you to be lazy

  112. AST transformations 47 The compiler will do the job for

    you
  113. AST transformations 47 More concise, more readable code

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

  115. 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
  116. 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() { /* ... */ } }
  117. Compare Java and Groovy 50

  118. Compare Java and Groovy 50

  119. Event listeners 51 button.setOnClickListener(new OnClickListener() { @Override public void onClick(View

    view) { Log.i(tag, "clicked!") } })
  120. Event listeners 51 button.onClickListener = { Log.i(tag, "clicked!") } button.setOnClickListener(new

    OnClickListener() { @Override public void onClick(View view) { Log.i(tag, "clicked!") } })
  121. Properties and equality 52 if (intent.getAction().equals(Intent.ACTION_VIEW)) { Log.i(tag, "view"); }

  122. 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") }
  123. Groovy Development Kit methods 53 new Thread() { public void

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

    run() { // … } }.start(); Thread.start { // … }
  125. Groovy’s with {} method 54 view = new TextView(context); view.setText(name);

    view.setTextSize(16f); view.setTextColor(Color.WHITE);
  126. 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 }
  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); }
  128. 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 }
  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 } } } }
  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) } }
  131. 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 }
  132. @glaforge — Groovy on Android What else?

  133. 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
  134. Swiss Knife — Arasthel / SwissKnife 59

  135. Swiss Knife — Arasthel / SwissKnife 59 Like Butter Knife

    & Android Annotations, but for Groovy
  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 } }
  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 } } View injection
  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 } } Execute code outside the UI thread
  139. 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
  140. Swiss Knife — available annotations ! • @OnUIThread • @OnBackground

    ! • @OnClick • @OnLongClick • @OnItemClick • @OnItemLongClick ! ! ! ! • @OnItemSelected • @OnChecked • @OnFocusChanged • @OnTouch • @OnPageChanged • @OnTextChanged • @OnEditorAction 61
  141. Swiss Knife — available annotations ! • @OnUIThread • @OnBackground

    ! • @OnClick • @OnLongClick • @OnItemClick • @OnItemLongClick ! ! ! ! • @OnItemSelected • @OnChecked • @OnFocusChanged • @OnTouch • @OnPageChanged • @OnTextChanged • @OnEditorAction 61 AST transformations
  142. Swiss Knife — setup 62 dependencies { … compile 'com.arasthel:swissknife:1.0.4'

    … }
  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)
  144. 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
  145. 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
  146. @glaforge — Groovy on Android Want more?

  147. Programming your Android applications in Groovy • You can use

    Groovy to code Android apps! • use Groovy 2.4.0-beta-1+ (latest: beta-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 65
  148. Demo conference application 66

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

  150. Demo Google Wear + Phone application 67

  151. Demo Google Wear + Phone application 67 Source code available:

    http://bit.ly/grooid-wear
  152. @glaforge — Groovy on Android Summary

  153. Summary ! • Groovy can make your Android code… •

    more concise & readable • more maintainable ! • Without compromising • type safety • speed 69
  154. Feedback welcome 70

  155. Feedback welcome 70 We need you to help figure out

    how Groovy can help you!
  156. @glaforge — Groovy on Android Q & A

  157. None
  158. 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
  159. 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 74