This is the third post of the Clojure on Android series.
Last time we used lein-droid to create a Clojure Android app and we ran it on your Android device, if you had one available.
Announcements and Slides
As I mentioned in the last post I spoke at the FunctionalKats conference about Clojure on Android, it was a lovely conference, thanks to Andrea's team's herculean efforts :)
The slides from that talk cover the material we already talked about and some more we'll cover in the future.
On that note, if you are/know a woman that is curious about Clojure and lives in Ireland, I'll be teaching at the Dublin ClojureBridge Workshop on October 2 and 3. It's a free introduction to Clojure, from any level of previous knowledge, so pass the word around, please! Non programmers welcome, too :)
Now onto the task at hand.
What you will get:
- running it on an emulator
- how to use Skummet to make a leaner
apk - how to setup interactive development with CIDER.
What you need:
You need to have followed the instructions in the previous post.
Android emulator
If you don't have an Android device to use for development, you can use the Android emulator.
A word of warning: performance is varied and unpredictable, for me sometimes it takes minutes to start.
If you hate slow emulators (who doesn't) don't despair yet, I'll show you an alternative just after this.
In order to use the android emulator you have to install these System Images from the android command:
- ARM EABI v7a System Image
- Google APIs ARM EABI v7a System Image
Open the emulator manager with android avd, a UI will show up which will allow you to specify screen resolution, and more for your new emulator. You can make many different versions of the emulator, and test your app behaviours with different resolution and more.
A better option
As I said the official Android emulator can be very slow and unresponsive.
Genymotion (many thanks to Alex Yakushev for the tip) is much faster, but you have to pay if you use it commercially. There's an indie license which is cheaper.
It works pretty much as the standard emulator, but with a slicker UI and a good number of presets.
Once you have your emulator running lein droid doall will launch the app on it, see previous post if that sounds unfamiliar.
adb Overview
Here is short overview of adb commands if you have never programmed on Android.
With adb you should be able to do all your android app related tasks from a command line.
adb devicesshows the attached devices. If your device doesn't show up on adb devices you probably need to enable usb debugging on its settingsadb usbwill set the adb mode to be through usbadb tcpip <port>will set the adb mode to be over wifi, it's convenient when you need a device not to be attached to usb while debuggingadb connect <device-ip-address>to connect to the device after doingadb tcpip <port>, then you can use all otheradbcommands as if it was attached to usbadb install <path-to-apk>pushes an android app to deviceadb forward <local> <remote>forwards socket connections from a specified local port to the remote port on the emulator/device instanceadb logcatwill show you all debugging messages, you can use some filtering, see refs.
lein droid Overview
We've only seen
lein new droidlein droid doall
But there are quite a few important commands, some that mirror the adb commands from above:
lein droid cleanto wipe all the compiled files, always useful to do that when something behaves oddly in your applein droid replwhich will open a repl connected with the clojure running on the device/emulatorlein droid buildcompiles your applein droid apkbuilds an apk out of the built applein droid installinstalls the apk on devicelein droid runruns the installed applein droid forward-portforwards to a local port from the remote android portlein droid with-profile <profile> <command>you can execute the commands using a profile that is in yourproject.clj, we'll use it for Skummet later.
As we saw last time many of these commands are called by lein droid doall, and you only need to call them one by one if you want more granularity than what doall gives you.
repl Overview
lein droid repl which we mentioned above, will open a REPL that runs directly on your device/emulator. It will look something like this:
Binding device port 9999 to local port 9999 ...
$ lein droid repl
(Warning: profile :android-common not found.)
(Warning: profile :android-user not found.)
REPL-y 0.3.7, nREPL 0.2.10
Clojure 1.7.0
Dalvik 0.9
Exit: Control+D or (exit) or (quit)
Commands: (user/help)
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
user=>
user=> (in-ns com.lambdacat.anfocal.main)
You need to use (in-ns... to call functions from main.clj. While there are things you can do just with a repl, using Emacs and CIDER allows you to have your app live update when changing something, see below for how to set it up.
Optional: faster Clojure Android apps with Skummet
The official instructions are here.
You basically need to add the Skummet options to the profiles section of your project.clj:
:lean
[:release
{:dependencies ^:replace [[org.skummet/clojure "1.7.0-RC3-r2" :use-resources true]
[neko/neko "4.0.0-alpha5"]]
:exclusions [[org.clojure/clojure]
[org.clojure-android/clojure]]
:jvm-opts ["-Dclojure.compile.ignore-lean-classes=true"]
:global-vars ^:replace {clojure.core/*warn-on-reflection* true}
:android {:lean-compile true
:skummet-skip-vars []}}]
to set up Proguard, that "shrinks, optimises, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names" you need to install JDK 7
and edit proguard-minify.cfg with the full path to rt.jar
-libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/rt.jar
Since Skummet is very much in development, if something doesn't work you might want to try using a SNAPSHOT version of lein-droid with it. Change
:plugins [[lein-droid "0.4.3"]]
to
:plugins [[lein-droid "0.4.3-SNAPSHOT"]]
as well.
Or ask in #clojure-android@freenode, alexyakushev, the author is often around and very helpful.
After that you can compile your app with:
lein with-profile lean do clean, droid doall
Which will be one complex, yet familiar, command if you read the section on lein droid above.
CIDER
For Clojure Android interactive development you'll need to use Emacs and CIDER.
First you have to get an Emacs version, if you don't already have one. If you're on Mac, Aquamacs is a good one.
Once you have an emacs install you have to get some package management for it, there is marmalade (which tends to have older version of packages), melpa (bleeding edge snapshots) and melpa stable (up-to-date release version), I suggest the latter.
If you follow the links you'll find instructions to install them.
For convenience, if you have Emacs >24, which you should if you installed Aquamacs, then just add to your ~/.emacs file:
(require 'package) ;; You might already have this line
(add-to-list 'package-archives
'("melpa-stable" . "https://stable.melpa.org/packages/") t)
(package-initialize) ;; You might already have this line
Hit M-x package-refresh-contents to make sure the list of packages updates.
Hook into device
Now you can install CIDER 0.9.1: official instructions.
Adding this below to your ~/.emacs file and rebooting Emacs could be enough.
(unless (package-installed-p 'cider)
(package-install 'cider))
Alternatively, if it's not, M-x package-list-packages and find CIDER with C-s cider, select it by typing i when you are on its line, and then x to execute, and you should see it installing.
Once CIDER is installed:
- open the
project.cljfile from the project in Emacs:C-x fand then fill in the path - open a shell into it with
M-x shell - run
lein droid doallon it and make sure your device/emulator is running the app - into that pane do
M-x cider-connect RET localhost RET 9999, the repl will open into that pane
If Emacs is new to you here are some more detailed instructions:
- you'll need 3 panes, 1 for the file to edit, 1 for the shell and 1 for the repl. It may be more convenient for you to have a separate terminal window to do
lein droid doallwith, if so you'll only need 2 panes - open the
project.cljfile from the project in Emacs:C-x fand then fill in the path - split the window horizontally with
C-x 2 - move to the lower buffer with
C-x o - split the pane vertically with
C-x 3 - choose the pane you want to open a shell into by moving to it with
C-x o - open a shell into it with
M-x shell - run
lein droid doallon it and make sure your device/emulator is running the app - then move to another pane with
C-x o - into that pane do
M-x cider-connect RET localhost RET 9999, the repl will open into that pane
And voila, you will be able to change code and see the changes on your app on the fly, let's see a simple example now.
Live coding "Hello World"
To make sure all of this worked, you need to change something in the main.clj file, so open it in Emacs: C-x f and then fill in the path, you can autocomplete with TAB, it should be under src/clojure/com/lambdacat/anfocal/main.clj.
You should have code similar to this:
(defactivity com.lambdacat.anfocal.MainActivity
:key :main
(onCreate [this bundle]
(.superOnCreate this bundle)
(neko.debug/keep-screen-on this)
(on-ui
(set-content-view! (*a)
[:linear-layout {:orientation :vertical
:layout-width :fill
:layout-height :wrap}
[:edit-text {:id ::user-input
:hint "Type text here"
:layout-width :fill}]
[:button {:text R$string/touch_me ;; We use resource here, but could
;; have used a plain string too.
:on-click (fn [_] (notify-from-edit (*a)))}]]))))
Try adding a background colour to the main layout, like this, under the orientation:
[:linear-layout {:orientation :vertical
:background-color (android.graphics.Color/parseColor "#ff2020")
:layout-width :fill
:layout-height :wrap}
and now put your cursor on the closing parenthesis of the (on-ui ... function and press C-x C-e, => true should appear in the mini-buffer at the bottom of Emacs and if you have your emulator/device visible you will see it update with the new colour.
Next Steps
Now you know your way around adb and lein droid, you have an interactive development environment set up and you know how to compile with Skummet.
What we want to do next is:
- Clojure Android interactive development examples
- some necessary background Android knowledge
Next post: (Coming Soon)
Thanks to alexyakushev and #clojure-android@freenode for the help.
Comments? Give me a shout at @lambda_cat.
To get the latest post updates subscribe to the LambdaCat newsletter.
You can support my writing on LambdaCat's Patreon.
