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

Groovy and Android: a winning pair

Groovy and Android: a winning pair

Talk given at #Devoxx Belgium 2014 about using the Groovy language to develop Android applications.

Avatar for Cédric Champeau

Cédric Champeau

November 14, 2014
Tweet

More Decks by Cédric Champeau

Other Decks in Programming

Transcript

  1. #DV14 #GroovyAndroid @CedricChampeau whoami.groovy def speaker = new Speaker( name:

    'Cedric Champeau', employer: 'Pivotal', occupation: 'Core Groovy committer', successes: ['Static type checker', 'Static compilation', 'Traits', 'Markup template engine', 'DSLs'], twitter: '@CedricChampeau', github: 'melix', extraDescription: '''Groovy in Action 2 co- author Misc OSS contribs (Gradle plugins, deck2pdf, jlangdetect, ...)''' ) 2
  2. #DV14 #GroovyAndroid @CedricChampeau Why Android? • Uses a JVM •

    SDK is free • Tooling also freely available (Android Studio) • Swift anyone? 3
  3. #DV14 #GroovyAndroid @CedricChampeau Why Groovy? • Built on top of

    the shoulders of a Giant (Java) • Runs a JVM • Android developers shouldn't be suffering • Java on Android is very verbose • And the main development language on the platform • Multi-faceted language • OO, Imperative, functional, scripting, dynamic, static, … • Straightforward integration with Java 4
  4. #DV14 #GroovyAndroid @CedricChampeau Why Groovy? button.setOnClickListener(new View.OnClickListener() { @Override void

    onClick(View v) { startActivity(intent); } }) 5 button.onClickListener = { startActivity(intent) }
  5. #DV14 #GroovyAndroid @CedricChampeau Why Groovy? @RestableEntity @ToString class User {

    String name String phone String avatar Integer balance static constraints = { name pattern: ~/[a-zA-Z]+/, min: 3, max: 5, blank: false, nullable: false phone pattern: ~/\d+/, size: 2..4 balance range: 10..100 } } 6 Groovy Beans Grails-like entities
  6. #DV14 #GroovyAndroid @CedricChampeau def user = new User(name: 'Name') form(R.id.user_form,

    user) { form -> editText(R.id.user_name).attach('name') editText(R.id.user_phone).attach('phone') editText(R.id.user_balance).attach('balance') form.submit(R.id.submit_button) { if (form.object.validate()) { this.showToast('Validated with success!') } else { form.object.errors.each { this.showToast(it.toString()) } } } } 7 declarative code Why Groovy?
  7. #DV14 #GroovyAndroid @CedricChampeau Intent viewIntent = new Intent(this, WearPresentationActivity.class); PendingIntent

    viewPendingIntent = PendingIntent.getActivity( this, 0, viewIntent, FLAG_UPDATE_CURRENT); NotificationCompat.BigTextStyle bigStyle = new NotificationCompat.BigTextStyle(); bigStyle.bigText("Time left for your presentation: "+timeLeft+"\n"+ "Elapsed time: "+rounded+"%"); NotificationCompat.Builder notificationBuilder= new NotificationCompat.Builder(this); notificationBuilder.setSmallIcon(R.drawable.ic_action_alarms) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.speaker)) .setContentTitle("Time left") .setContentText(timeLeft+" (Elapsed: "+rounded+"%)") .setContentIntent(viewPendingIntent) .setStyle(bigStyle); NotificationManagerCompat notificationManager= NotificationManagerCompat.from(this); notificationManager.notify(NOTIFICATION_ID,notificationBuilder.build()); 8 Why Groovy?
  8. #DV14 #GroovyAndroid @CedricChampeau notify(NOTIFICATION_ID) { smallIcon = R.drawable.ic_action_alarms largeIcon =

    cachedBitmap contentTitle = 'Time left' contentText = "$timeLeft (Elapsed: ${rounded}%)" contentIntent = pendingActivityIntent(0, intent(WearPresentationActivity), FLAG_UPDATE_CURRENT) ongoing = true style = bigTextStyle { bigText """Time left for your presentation: $timeLeft Elapsed time: ${rounded}%) """ } } 9 Why Groovy?
  9. #DV14 #GroovyAndroid @CedricChampeau @CompileStatic class ContextGroovyMethods { static NotificationManagerCompat getCompatNotificationManager(Context

    self) { NotificationManagerCompat.from(self) } static void notify(Context self, int notificationId, Notification notification) { getCompatNotificationManager(self).notify(notificationId, notification) } static Notification notification(Context self, @DelegatesTo(NotificationCompat.Builder) Closure notificationSpec) { def builder = new NotificationCompat.Builder(self) builder.with(notificationSpec) builder.build() } static void notify(Context self, int notificationId, @DelegatesTo(NotificationCompat.Builder) Closure notificationSpec) { notify(self, notificationId, notification(self, notificationSpec)) } ... } 10 Why Groovy?
  10. #DV14 #GroovyAndroid @CedricChampeau Groovy on Android: the problems • Groovy

    is a dynamic language • Not everything done at compile time • Intensive use of reflection • Potentially slow invocation pathes • Battery? • Bytecode is different • Classes at runtime? 13
  11. #DV14 #GroovyAndroid @CedricChampeau • Not all classes are available •

    java.bean.xxx very problematic • Multiple runtimes • Dalvik • ART • Behavior not the same as the standard JVM 14 Groovy on Android: the problems
  12. #DV14 #GroovyAndroid @CedricChampeau Groovy on Android: discobot • Early days

    • Written in 2011 • Fork of Groovy 1.7 • Capable of running scripts at runtime • but slow... 15
  13. #DV14 #GroovyAndroid @CedricChampeau Groovy on Android: dex files • Dalvik

    VM = alternative bytecode • Groovy generates JVM bytecode • Translation done through dex • No native support for generating classes at runtime 16
  14. #DV14 #GroovyAndroid @CedricChampeau Discobot process • Write Groovy bytes to

    a file • Package those into a jar • Use a special classloader to load the class • Enjoy! 19
  15. #DV14 #GroovyAndroid @CedricChampeau Discobot process • Works, but very slow

    • Lots of I/O involved • What about ASMDex? • Same approach used by Ruboto • Nice proof of concept 21
  16. #DV14 #GroovyAndroid @CedricChampeau Groovy 2.4: Objectives for Android • Supporting

    Android in the standard distribution • Building a full Android application in Groovy • Main focus on @CompileStatic • Optional use of dynamic Groovy 23
  17. #DV14 #GroovyAndroid @CedricChampeau Groovy 2.4: Objectives for community • Community

    is a major strenght of Groovy • We need you for Android too! • Bring the goodness of Groovy to Android • Invent new frameworks! 24
  18. #DV14 #GroovyAndroid @CedricChampeau Requirements • Gradle • Android Studio •

    Or your favorite editor... • Groovy 2.4.0-beta-3 • A good tutorial on Android... 28
  19. #DV14 #GroovyAndroid @CedricChampeau Groovy 2.4 Android Support • Must use

    a specific Android jar • Use of the grooid classifier • Replaces java.beans use with openbeans • Workarounds for Android specific behavior • Reduced number of methods in bytecode • Important for the 64k limit of dex files 29
  20. #DV14 #GroovyAndroid @CedricChampeau Gradle plugin • Gradle is the new

    default build system for Android • apply plugin: 'com.android.application' • Uses a non standard compilation process • Without Groovy specific plugin, lots of trickery involved • Thus apply plugin: apply plugin: 'me.champeau.gradle.groovy-android' • Supports both the application and library plugins 30
  21. #DV14 #GroovyAndroid @CedricChampeau Gradle plugin buildscript { repositories { jcenter()

    } dependencies { classpath 'com.android.tools.build:gradle:0.14.0' classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.4' } } apply plugin: 'me.champeau.gradle.groovy-android' dependencies { compile 'org.codehaus.groovy:groovy:2.4.0-beta-3:grooid' } 31
  22. #DV14 #GroovyAndroid @CedricChampeau Then code! @CompileStatic @ToString(includeNames = true) @EqualsAndHashCode

    class Session { Long id Long speakerId Slot slot String title String summary List<String> tags } 32
  23. #DV14 #GroovyAndroid @CedricChampeau Groovifying Android APIs 33 class FeedTask extends

    AsyncTask<String, Void, String> { protected String doInBackground(String... params) { // very long boilerplate code.... } @Override protected void onPostExecute(String s) { mTextView.setText(s); } }
  24. #DV14 #GroovyAndroid @CedricChampeau Groovifying Android APIs 34 Fluent.async { def

    json = new JsonSlurper().parse([:], new URL('http://path/to/feed'), 'utf-8') json.speakers.join(' ') } then { mTextView.text = it }
  25. #DV14 #GroovyAndroid @CedricChampeau System resources • Example of the GR8Conf

    Agenda application • Groovy jar: 4.5MB • Application size: 2MB! • After ProGuard: only 1MB! • ~8.2MB of RAM! (but lots of images) 36
  26. #DV14 #GroovyAndroid @CedricChampeau Community projects • Community is more important

    than the language • New frameworks to invent • Some already did! 38
  27. #DV14 #GroovyAndroid @CedricChampeau SwissKnife • Similar to Android Annotations and

    ButterKnife • Based on AST transformations • View injection • Threading model • Works with annotations to generate code 39
  28. #DV14 #GroovyAndroid @CedricChampeau SwissKnife 40 class MyActivity extends Activity {

    @ViewById(R.id.myField) TextField mTextField @OnClick(R.id.button) void onButtonClicked(Button button) { Toast.makeText(this, "Button 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) contentView = R.layout.activity_main // This must be called for injection of views and callbacks to take place SwissKnife.inject(this) } }
  29. #DV14 #GroovyAndroid @CedricChampeau Grooid Tools 41 View view = new

    AndroidBuilder().build(this) { relativeLayout(width: MATCH_PARENT, height: MATCH_PARENT, padding: [dp(64), dp(16)]) { textView(width: MATCH_PARENT, height: dp(20), text: R.string.hello_world) } } • Builders for views • Experimental • https://github.com/karfunkel/grooid-tools
  30. #DV14 #GroovyAndroid @CedricChampeau Potential issues 42 • Performance of dynamic

    Groovy on low end-devices • Use @CompileStatic whenever possible • The infamous 64k method count • Use ProGuard and multidex support • Tooling support • Groovy not fully supported by Android Studio • Google support • Android Gradle plugin updates are very frequent
  31. #DV14 #GroovyAndroid @CedricChampeau “Best of all, I expect to try

    to update Android Studio right before the talk, so I have the latest possible version in the so­called Canary channel. What could possibly go wrong?” Ken Kousen, September 10th, 2014
  32. #DV14 #GroovyAndroid @CedricChampeau Other ideas 44 • Dagger-like dependency injection

    framework? • Data binding APIs • Improved reactive APIs • You can already use Reactor or RxJava