Slide 1

Slide 1 text

#GroovyAndroid @CedricChampeau Groovy & Android A winning pair By Cédric Champeau

Slide 2

Slide 2 text

#GroovyAndroid @CedricChampeau 2/47 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'], failures: Stream.of(bugs), twitter: '@CedricChampeau', github: 'melix', extraDescription: '''Groovy in Action 2 co- author Misc OSS contribs (Gradle plugins, deck2pdf, jlangdetect, ...)''' )

Slide 3

Slide 3 text

#GroovyAndroid @CedricChampeau 3/47 Why Android? • Uses a JVM • SDK is free • Tooling also freely available (Android Studio) • Swift anyone?

Slide 4

Slide 4 text

#GroovyAndroid @CedricChampeau 4/47 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

Slide 5

Slide 5 text

#GroovyAndroid @CedricChampeau 5/47 Why Groovy? button.setOnClickListener(new View.OnClickListener() { @Override void onClick(View v) { startActivity(intent); } }) button.onClickListener = { startActivity(intent) }

Slide 6

Slide 6 text

#GroovyAndroid @CedricChampeau 6/47 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 } } Groovy Beans Grails-like entities

Slide 7

Slide 7 text

#GroovyAndroid @CedricChampeau 7/47 @CompileStatic @SelfType(Context) trait GoogleApiProvider { GoogleApiClient googleApiClient void createGoogleApi() { googleApiClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .build() } ... } Why Groovy?

Slide 8

Slide 8 text

#GroovyAndroid @CedricChampeau 8/47 class CountDownService extends Service implements GoogleApiProvider { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) contentView = R.layout.activity_presentation createGoogleApi() } // ... } class StartPresentationActivity extends Activity implements GoogleApiProvider { // ... } class WearPresentationActivity extends Activity implements GoogleApiProvider, MessageApi.MessageListener { // ... } Why Groovy?

Slide 9

Slide 9 text

#GroovyAndroid @CedricChampeau 9/47 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()); Why Groovy?

Slide 10

Slide 10 text

#GroovyAndroid @CedricChampeau 10/47 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}%) """ } } Why Groovy?

Slide 11

Slide 11 text

#GroovyAndroid @CedricChampeau 11/47 @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)) } ... } Why Groovy?

Slide 12

Slide 12 text

#GroovyAndroid @CedricChampeau 12/47 private @Lazy Bitmap cachedBitmap = BitmapFactory.decodeResource(resources, R.drawable.speaker) Why Groovy?

Slide 13

Slide 13 text

#GroovyAndroid @CedricChampeau 13/47 SpeakerTime github.com/melix/speakertime

Slide 14

Slide 14 text

#GroovyAndroid @CedricChampeau 14/47 Groovy on Android: the problems • Groovy is primarily a dynamic language • Not everything done at compile time • Intensive use of reflection • Potentially slow invocation pathes • Battery? • Bytecode is different • Classes at runtime?

Slide 15

Slide 15 text

#GroovyAndroid @CedricChampeau 15/47 • Not all classes are available • java.bean.xxx very problematic • Multiple runtimes • Dalvik • ART • Behavior not the same as the standard JVM Groovy on Android: the problems

Slide 16

Slide 16 text

#GroovyAndroid @CedricChampeau 16/47 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

Slide 17

Slide 17 text

#GroovyAndroid @CedricChampeau 17/47 Compiling an Android application • Classic process

Slide 18

Slide 18 text

#GroovyAndroid @CedricChampeau 18/47 • Classic process for a Groovy application Compiling an Android application

Slide 19

Slide 19 text

#GroovyAndroid @CedricChampeau 19/47 Compiling an Android application • Runtime generation of classes

Slide 20

Slide 20 text

#GroovyAndroid @CedricChampeau 20/47 Classes at runtime?! • Works, but very slow • Lots of I/O involved • What about ASMDex? • Same approach used by Ruboto • Nice proof of concept

Slide 21

Slide 21 text

#GroovyAndroid @CedricChampeau 21/47 Redefinining objectives

Slide 22

Slide 22 text

#GroovyAndroid @CedricChampeau 22/47 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 • No performance issue!

Slide 23

Slide 23 text

#GroovyAndroid @CedricChampeau 23/47 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!

Slide 24

Slide 24 text

#GroovyAndroid @CedricChampeau 24/47

Slide 25

Slide 25 text

#GroovyAndroid @CedricChampeau 25/47 github.com/melix/grooidshell-example

Slide 26

Slide 26 text

#GroovyAndroid @CedricChampeau 26/47 Requirements • Gradle • Android Studio • Or your favorite editor... • Groovy 2.4.0 (“grooid”) • A good tutorial on Android...

Slide 27

Slide 27 text

#GroovyAndroid @CedricChampeau 27/47 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

Slide 28

Slide 28 text

#GroovyAndroid @CedricChampeau 28/47 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 use a plugin: apply plugin: 'groovyx.grooid.groovy-android' • Supports both the application and library plugins

Slide 29

Slide 29 text

#GroovyAndroid @CedricChampeau 29/47 Gradle plugin buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.14.0' classpath 'org.codehaus.groovy:gradle-groovy-android-plugin:0.3.5' } } apply plugin: 'groovyx.grooid.groovy-android' dependencies { compile 'org.codehaus.groovy:groovy:2.4.0:grooid' }

Slide 30

Slide 30 text

#GroovyAndroid @CedricChampeau 30/47 Then code! @CompileStatic @ToString(includeNames = true) @EqualsAndHashCode class Session { Long id Long speakerId Slot slot String title String summary List tags }

Slide 31

Slide 31 text

#GroovyAndroid @CedricChampeau 31/47 Groovifying Android APIs class FeedTask extends AsyncTask { protected String doInBackground(String... params) { // very long boilerplate code.... } @Override protected void onPostExecute(String s) { mTextView.setText(s); } }

Slide 32

Slide 32 text

#GroovyAndroid @CedricChampeau 32/47 Groovifying Android APIs 32 Fluent.async { def json = new JsonSlurper().parse([:], new URL('http://path/to/feed'), 'utf-8') json.speakers.join(' ') } then { mTextView.text = it }

Slide 33

Slide 33 text

#GroovyAndroid @CedricChampeau 33/47 Performance?

Slide 34

Slide 34 text

#GroovyAndroid @CedricChampeau 34/47 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)

Slide 35

Slide 35 text

#GroovyAndroid @CedricChampeau 35/47 Community

Slide 36

Slide 36 text

#GroovyAndroid @CedricChampeau 36/47 Community projects • Community is more important than the language • New frameworks to invent • Some already did!

Slide 37

Slide 37 text

#GroovyAndroid @CedricChampeau 37/47 SwissKnife • Combines ideas from Android Annotations and ButterKnife • Based on AST transformations • View injection • Threading model • Bundles management • Works with annotations to generate code

Slide 38

Slide 38 text

#GroovyAndroid @CedricChampeau 38/47 SwissKnife 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) } }

Slide 39

Slide 39 text

#GroovyAndroid @CedricChampeau 39/47 SwissKnife @SaveInstance private int myInt // You can also set a custom tag to your variable @SaveInstance("MYSTRING") private String myString @Override void onCreate(Bundle savedInstanceState){ // Your previous code SwissKnife.restoreState(this, savedInstanceState) }

Slide 40

Slide 40 text

#GroovyAndroid @CedricChampeau 40/47 SwissKnife public class ParcelableClass implements Parcelable { private int id; private String name; public ParcelableClass(Parcel source) { this.id = source.readInt(); this.name = source.readString(); } public void writeToParcel(Parcel out) { out.writeInt(id); out.writeString(name); } public String getId() { return id; } // ... }

Slide 41

Slide 41 text

#GroovyAndroid @CedricChampeau 41/47 SwissKnife @Parcelable class ParcelableClass { int id String name }

Slide 42

Slide 42 text

#GroovyAndroid @CedricChampeau 42/47 Grooid Tools 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

Slide 43

Slide 43 text

#GroovyAndroid @CedricChampeau 43/47 Potential issues • 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

Slide 44

Slide 44 text

#GroovyAndroid @CedricChampeau 44/47 “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

Slide 45

Slide 45 text

#GroovyAndroid @CedricChampeau 45/47 Other ideas • Dagger-like dependency injection framework? • Dagger 2 in the works... • Data binding APIs • Improved reactive APIs • You can already use Reactor or RxJava

Slide 46

Slide 46 text

#GroovyAndroid @CedricChampeau 46/47 Future is now! • New York Times next app being written in Groovy!

Slide 47

Slide 47 text

#GroovyAndroid @CedricChampeau @CedricChampeau melix http://melix.github.io/blog