Save 37% off PRO during our Black Friday Sale! »

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?

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

December 12, 2014
Tweet

Transcript

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

  2. None
  3. Total Android Newbie!

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

  6. Is there something we can do about it?

  7. Yes, Groovy!

  8. New York Times — Getting Groovy with Android 4

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

  10. New York Times — Getting Groovy with Android 5

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

    York Times recruits a Groovy / Android expert http://bit.ly/nyt-job
  12. 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);
  13. What does NYT likes about Groovy on Android? • No

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  34. With a Hello World? 16

  35. Android Studio 17

  36. Android Studio 17 Click on « New Project »

  37. Android Studio 18

  38. Android Studio 18 Fill in the project details

  39. Android Studio 19

  40. Android Studio 19 Choose your target platform

  41. Android Studio 20

  42. Android Studio 20 Create a 
 blank activity

  43. Android Studio 21

  44. Android Studio 21 Details for your new activity

  45. Android Studio 22

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

  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']) }
  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' } }
  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' } }
  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' } }
  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
  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 }
  53. Run your Groovy-powered app in the emulator 26

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

    work?
  57. Standard process 30 Compile-time Runtime

  58. Standard process 30 Compile-time Runtime .class

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

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

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

  62. First attempt — DiscoBot 31 Compile-time Runtime

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

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

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

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

    .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 « Ruboto » approach, slow & inneficient
  69. New process — just like Java! 32 Compile-time Runtime

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

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

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

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

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

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

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

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

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

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

  80. What does Groovy bring? 35 Optional

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

  82. What does Groovy bring? 35 Optional Parentheses

  83. What does Groovy bring? 35 Optional Typing

  84. What does Groovy bring? 35 Optional return keyworkd

  85. What does Groovy bring? 35 Optional public keyworkd

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

  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
  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)
  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!
  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
  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
  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 }
  95. Builders

  96. Builders With closures come Groovy « builders »!

  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' } }
  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" } } }
  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'
  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!
  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!
  102. Annotations

  103. Annotations Actually, code transformations triggered by annotations

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

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

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

  110. AST transformations 47 Groovy allows you to be lazy

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

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

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

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

  117. Compare Java and Groovy 50

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

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

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

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

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

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

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

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

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

    & Android Annotations, but for Groovy
  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 } }
  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
  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
  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
  139. Swiss Knife — available annotations • @OnUIThread • @OnBackground •

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

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

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

  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
  147. Demo conference application 66

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

  149. Demo Google Wear + Phone application 67

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

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

  152. Summary • Groovy can make your Android code… • more

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

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

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

  156. None
  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
  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