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

Clojure on Android

Clojure on Android

Tools, techniques and practices to write Android apps in Clojure.

Alexander Yakushev

June 25, 2015
Tweet

More Decks by Alexander Yakushev

Other Decks in Programming

Transcript

  1. INTRO Have been participating in GSoC for 4 years on

    Clojure's behalf. Worked on Clojure-Android during years 1 and 2 Wrote Project Skummet last year Now working on enabling TDD and CI support for Clojure- Android projects Author of Compliment library Graduated a week ago, now have to figure what I'm doing with my life
  2. WHAT IS CLOJURE-ANDROID? Umbrella term for multiple projects: our own

    Clojure forks a build tool idiomatic wrapper library different dev tooling, etc. Started in 2011 by Daniel Solano Gómez.
  3. WHY CLOJURE INSTEAD OF JAVA? Because we can! All advantages

    of Clojure the language. REPL-driven development. Logic, data and UI are all in one language.
  4. WHAT ABOUT CLOJURESCRIPT + REACT NATIVE/PHONEGAP/…? Arguments for Clojure-Android: Closer

    to native platform Easier to learn for Android developers [Arguably] Better tooling support [Unconfirmed] Simpler interop [Unconfirmed] Suits better for a multithreaded environment
  5. WHAT ABOUT CLOJURESCRIPT + REACT NATIVE/PHONEGAP/…? Arguments for ClojureScript +

    X: Faster startup time Easier to dive in for web developers Potential to write (kinda-)cross-platform applications [Unconfirmed] Access to JavaScript libraries @swannodette
  6. GOALS OF CLOJURE-ANDROID 1. Let Android developers write good apps

    in Clojure. 2. Let Clojure developers write good Android apps.
  7. LEIN-DROID CRASH COURSE Show help: l e i n d

    r o i d h e l p Create a new project: l e i n d r o i d n e w A w e s o m e A p p c o m . m e g a c o r p . a w e s o m e a p p Do everything else: l e i n d r o i d d o a l l
  8. NEKO https://github.com/clojure-android/neko Neko is a Clojure-Android toolkit library. Wraps different

    Android API facilities. Takes care of dynamic compilation, REPL, CIDER.
  9. NEKO.UI This is how Android XML UI looks like: <

    ? x m l v e r s i o n = " 1 . 0 " e n c o d i n g = " u t f - 8 " ? > < L i n e a r L a y o u t x m l n s : a n d r o i d = " h t t p : / / s c h e m a s . a n d r o i d . c o m / a p k / r e s / a n d r o i d " a n d r o i d : o r i e n t a t i o n = " v e r t i c a l " > < E d i t T e x t a n d r o i d : i d = " @ + i d / n a m e " a n d r o i d : h i n t = " Y o u r n a m e " a n d r o i d : l a y o u t _ w i d t h = " f i l l _ p a r e n t " a n d r o i d : l a y o u t _ h e i g h t = " w r a p _ c o n t e n t " > < / E d i t T e x t > < B u t t o n a n d r o i d : i d = " @ + i d / s e n d " a n d r o i d : t e x t = " S e n d " > < / B u t t o n > < / L i n e a r L a y o u t >
  10. NEKO.UI And this is neko.ui declaration: [ : l i

    n e a r - l a y o u t { : o r i e n t a t i o n : v e r t i c a l } [ : e d i t - t e x t { : i d : : n a m e : l a y o u t - w i d t h : f i l l : h i n t " Y o u r n a m e " } ] [ : b u t t o n { : i d : : s e n d : t e x t " S e n d " : o n - c l i c k ( f n [ _ ] ( s e n d - i n p u t . . . ) ) } ] ]
  11. NEKO.UI Able to do this: ( c o n c

    a t [ : l i n e a r - l a y o u t { : o r i e n t a t i o n : h o r i z o n t a l } ] ( f o r [ i ( r a n g e 1 0 ) ] [ : b u t t o n { : t e x t ( s t r i ) : l a y o u t - w i d t h [ 3 5 : d p ] : o n - c l i c k ( f n [ _ ] ( t o a s t ( s t r " C l i c k e d " i ) ) ) } ] ) )
  12. NEKO.UI Or even some really crazy stuff: ( l e

    t [ l a n d s c a p e ? ( = ( n e k o . u i / g e t - s c r e e n - o r i e n t a t i o n ) : l a n d s c a p e ) ] [ : r e l a t i v e - l a y o u t { } [ : e d i t - t e x t { : i d : : u s e r n a m e : h i n t " Y o u r n a m e " } ] [ : b u t t o n { : i d : : s e n d : t e x t " S e n d " ( i f l a n d s c a p e ? : l a y o u t - t o - r i g h t - o f : l a y o u t - b e l o w ) : : u s e r n a m e } ] ] )
  13. NEKO.UI Accessing created UI elements: ( r e q u

    i r e ' [ n e k o . f i n d - v i e w : r e f e r [ f i n d - v i e w ] ] ) ( f i n d - v i e w a c t i v i t y : : n a m e ) = > # o b j e c t [ a n d r o i d . w i d g e t . E d i t T e x t . . . ] ( c o n f i g ( f i n d - v i e w a c t i v i t y : : s e n d ) : e n a b l e d f a l s e )
  14. NEKO.DATA.SQLITE By default, working with SQLite on Android means: Writing

    SQL to create tables Writing SQL to query the database Awkwardly extracting data from Cursor C u r s o r c u r s o r = d b . q u e r y ( " e m p l o y e e s " , n e w S t r i n g [ ] { " n a m e " , " s a l a r y " , " a c t i v e " } , " _ i d = % " , 1 ) ; c u r s o r . m o v e T o N e x t ( ) ; S t r i n g n a m e = c u r s o r . g e t S t r i n g ( 0 ) ; l o n g s a l a r y = c u r s o r . g e t L o n g ( 1 ) ; b o o l e a n a c t i v e = c u r s o r . g e t B o o l e a n ( 2 ) ;
  15. NEKO.DATA.SQLITE In Neko you can define a schema: ( r

    e q u i r e ' [ n e k o . d a t a . s q l i t e : a s d b ] ) ( d e f d b - s c h e m a ( d b / m a k e - s c h e m a : n a m e " l o c a l . d b " : v e r s i o n 1 : t a b l e s { : e m p l o y e e s { : c o l u m n s { : _ i d " i n t e g e r p r i m a r y k e y " : n a m e " t e x t n o t n u l l " : v a c a t i o n " b o o l e a n " : b o s s _ i d " i n t e g e r " } } : b o s s e s { : c o l u m n s { : _ i d " i n t e g e r p r i m a r y k e y " : n a m e " t e x t n o t n u l l " } } } ) ) ( d e f g e t - d b - h e l p e r ( m e m o i z e ( f n [ ] ( d b / c r e a t e - h e l p e r d b - s c h e m a ) ) ) ) ( d e f n g e t - d b [ ] ( d b / g e t - d a t a b a s e ( g e t - d b - h e l p e r ) : w r i t e ) )
  16. NEKO.DATA.SQLITE Inserting rows: ( d b / i n s

    e r t ( g e t - d b ) : e m p l o y e e s { : n a m e " S h e l l e y L e v e n e " : v a c a t i o n f a l s e } ) Updating rows: ; ; S e n d e m p l o y e e s w i t h I D s 1 , 5 a n d 7 o n v a c a t i o n ( d b / u p d a t e ( g e t - d b ) : e m p l o y e e s { : v a c a t i o n t r u e } { : _ i d [ : o r 1 5 7 ] } ) Querying database: ; ; F i n d a l l e m p l o y e e s n o t o n v a c a t i o n ( d b / q u e r y - s e q ( g e t - d b ) : e m p l o y e e s { : v a c a t i o n f a l s e } ) = > ( { : _ i d 5 , : n a m e " D a v e M o s s " , : v a c a t i o n f a l s e } { : _ i d 8 , : n a m e " R i c k y R o m a " , : v a c a t i o n f a l s e } )
  17. NEKO.DATA.SHARED-PREFS Provides an atom wrapper for SharedPreferences: ( d e

    f p r e f e r e n c e s m y - p r e f s " p r e f _ f i l e " ) ( s w a p ! m y - p r e f s a s s o c : l a s t - u s e r " j o e " ) ; ; A p p l i c a t i o n i s r e s t a r t e d a t s o m e p o i n t . ( : l a s t - u s e r @ m y - p r e f s ) = > " j o e "
  18. NEKO.DIALOG.ALERT ( - > ( a l e r t

    - d i a l o g - b u i l d e r a c t i v i t y { : m e s s a g e " D i a l o g m e s s a g e " : c a n c e l a b l e t r u e : p o s i t i v e - t e x t " O K " : p o s i t i v e - c a l l b a c k ( f n [ _ _ _ ] ( t o a s t " C l i c k e d O K " ) ) : n e g a t i v e - t e x t " C a n c e l " : n e g a t i v e - c a l l b a c k ( f n [ d i a l o g _ ] ( . c a n c e l d i a l o g ) ) } ) . c r e a t e . s h o w ) )
  19. HOW CLOJURE COMPILES VARS From c l o j u

    r e / c o r e _ _ i n i t . c l a s s : c o n s t _ _ 1 5 8 1 = ( V a r ) R T . v a r ( " c l o j u r e . c o r e " , " p r - s t r " ) ; c o n s t _ _ 1 5 8 4 = ( A F n ) R T . m a p ( n e w O b j e c t [ ] { . . . } ) ; . . . c o n s t _ _ 1 5 8 1 . s e t M e t a ( ( I P e r s i s t e n t M a p ) c o n s t _ _ 1 5 8 4 ) ; c o n s t _ _ 1 5 8 1 . b i n d R o o t ( n e w c o r e . p r _ s t r ( ) ) ; From code that uses c l o j u r e . c o r e / p r - s t r : V a r p r _ s t r = ( V a r ) R T . v a r ( " c l o j u r e . c o r e " , " p r - s t r " ) ; . . . ( ( I F n ) p r _ s t r . g e t R a w R o o t ( ) ) . i n v o k e ( . . . ) ; Huge part of Clojure loading time comes from initializing vars http://blog.ndk.io/2014/02/25/clojure- bootstrapping.html
  20. HOW SKUMMET COMPILES VARS From c l o j u

    r e / c o r e $ p r _ s t r . c l a s s : p u b l i c s t a t i c I F n _ _ i n s t a n c e = n e w c o r e . p r _ s t r ( ) ; From code that uses c l o j u r e . c o r e / p r - s t r : c o r e $ p r _ s t r . _ _ i n s t a n c e . i n v o k e ( . . . ) ;
  21. BENEFITS OF LEAN COMPILATION Less time spent initializing vars Compiled

    _ _ i n i t classes are smaller Dereferencing vars is faster Smaller memory footprint Functions aren't anymore tied to namespaces which allows to use Proguard
  22. OTHER SKUMMET FEATURES Doesn't emit macros Doesn't emit r e

    f e r statements Can mark specific vars as non-lean if they are used as Var objects. Can compile basically anything that doesn't call e v a l in the code, e.g.: Clojure, core.async
  23. SKUMMET BENCHMARKS On Android: Clojure Skummet Reduction Load time, seconds

    4.02 1.00 75.1% APK size, KB 1669 696 58.3% Heap usage, KB 5469 2750 49.7% More rigorous and extensive benchmarks: http://blog.ndk.io/2015/04/23/state-of-coa.html
  24. CLARITY KEYBOARD BETA https://play.google.com/store/apps/details?id=com.swiftkey.clarity.keyboard Created by Adam Clements and his

    team at Swiftkey. Utilizes NLP to auto-correct whole phrases. Built with lein-droid, core.async, Skummet. Read more http://swiftkey.com/en/blog/what-makes-clarity-keyboard-tick-clojure/
  25. WHERE TO WRITE CLOJURE- ANDROID PROJECTS? CIDER Any other nREPL

    client: fireplace.vim, Counterclockwise, Cursive. Nightcode
  26. DOCUMENTATION AND HELP Lein-droid and Neko wikis and Marginalia docs

    #clojure-android IRC at Freenode clojure-android at Google Groups http://clojure-android.info/
  27. FUTURE PLANS More Skummet improvements (make it even faster) Boot-droid

    Bring the new hip things to Neko, e.g. Material design Propose a reliable asynchronous mechanism for Clojure- Android projects Become obscenely rich and make my own mobile platform that runs Clojure
  28. WE NEED PEOPLE! To bring up new usecases. To write

    apps. To contribute code and docs. To discover and report bugs. To prove that Clojure is a first-class language for Android.
  29. ACKNOWLEDGMENTS Daniel Solano Gómez and Zach Oakes for GSoC mentorship

    and their work. Adam Clements, Artur Malabarba, and the rest Clojure- Android contributors. Chas Emerick for nREPL. Bozhidar Batsov and Tim King for nrepl.el/CIDER. Rich Hickey and all Clojure community for creating Clojure and keeping it awesome.