Building native Android applications with Mirah and Pindah


Published on

So you want to build native Android applications without using Java? Here's how.

Published in: Technology, Education
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Building native Android applications with Mirah and Pindah

  1. 1. So you want to build Android apps… without Java?<br />Nick Plante @zapnap<br />(A brief introduction to Mirah and Pindah)<br />
  2. 2. Who?<br />Nick Plante<br />(not a Java developer)<br />Zerosum Labs<br />Rails Rumble<br /><br />Chinaccelerator<br />Contact me!<br />@zapnap on Twitter<br /><br />
  3. 3. Why?<br />Because you’re a Ruby developer (familiarity).<br />Because Android is awesome.<br />Because simplicity is elegance.<br />“Think Different.”<br />
  4. 4. Java vs Ruby<br />Dynamic vs Static<br />Simplicity vs Complexity & Verbosity<br />Less Ceremony<br />Java is a systems programming language, after all<br />But… The JVM is an awesome platform<br />Android’s use of the JVM (Dalvik) gives us options<br />
  5. 5. Java Alternatives<br />Android Scripting Environment (SL4A)<br />Great for scripting; not great for applications<br />Limited access to Android API / GUI<br />Performance issues<br />Other JVM-based languages:<br />Scala<br />Clojure<br />JRuby<br />Mirah<br />Groovy?<br />
  6. 6. JRuby & MrRuboto<br />Ruboto is your friend!<br />JRuby interface / framework for Android APIs<br /><br />But JRuby runtime overhead is a problem<br />Slow startup (~10 seconds)<br />Large APK size<br />HelloWorld: 3.4MB compressed, 10MB installed<br />
  7. 7. Introducing Mirah<br />Mirah compiles straight to Java bytecode<br />Very fast, no extra overhead<br />Syntax is very Ruby-ish<br />Statically-typed with local type inference<br />“Ruby with type annotations”<br />No runtime library<br />Mix and match Java code<br />
  8. 8.
  9. 9. Warning!<br />Mirah is still a very young language (v0.0.7)<br />Tooling is very, very alpha<br />Advantage: Eclipse (Java)<br />Redcar looks promising<br />Compilation errors are very, very not-so-fun<br />NativeException: jmeta.SyntaxError: expected Ensure before ' { |a b| Inte' (at line: 16, char: 40)<br />
  10. 10. One thing at a time<br />We’ll get to Android in just a second<br />First let’s see some basic Mirah syntax…<br />
  11. 11. # fib.mirah<br />def fib(a:int):int<br /> if a < 2<br /> a<br /> else<br /> fib(a-1) + fib(a-2)<br /> end<br />end<br />puts fib(20)<br />Ruby vsMirah<br />Parameter type declaration???<br />SRSLY? <br /># fib.ruby<br />def fib(a)<br /> if a < 2<br /> a<br /> else<br /> fib(a-1) + fib(a-2)<br /> end<br />end<br />puts fib(20)<br />Return type can often be inferred.<br />
  12. 12. .class output<br />Produces a java .class<br />$ mirahcfib.mirah<br />public static intfib(int)<br />Can also produce .java code<br />$ mirahc -jfib.mirah<br />* I have no idea why you would want to do this.<br />
  13. 13. .to_java => “ick”<br />// Generated from hello.mirah<br />public class Hello extends java.lang.Object {<br />public static void main(java.lang.String[] argv) {<br /> temp$1 = java.lang.System.out;<br /> temp$1.println(Hello.fib(20));<br /> }<br /> public static intfib(int a) {<br /> return (a < 2) ? (a) : ((Hello.fib((a - 1)) + Hello.fib((a - 2))));<br /> }<br />}<br />
  14. 14. Challenges / Major Differences<br />Java stdlib: both a blessing and a curse<br />List, HashMap, etc<br />arr.get(1) vs arr[0]<br />Blocks work, but syntactic sugar required<br />For example, List#eachcan be used (array)<br />But HashMap#eachdoes not exist<br />No optional arguments, no *splats, no ranges<br />
  15. 15. Using Java’s Standard Library<br />import java.util.Collections<br />import java.util.HashMap<br />import java.util.List<br />class ListPrinter<br /> def print(list:List)<br /> puts "first item: #{list.get(0)}"<br />list.each do |item|<br /> puts "item: #{item}"<br /> end <br /> end <br />end<br />class HashPrinter<br /> def print(map:HashMap)<br />map.keySet.each do |key|<br /> puts "#{key}: #{map.get(key)}"<br /> end <br /> end <br />end<br />map = { 'batman' => 'brucewayne', 'superman' => 'clarkkent' }<br /><br />list = ['peter', 'stewie', 'brian']<br /><br />
  16. 16. Get Mirah<br /># Install JRuby 1.6 if you haven’t already<br /># (or rvm use jruby)<br />$ jruby –S gem install mirah<br />$ mirah-e "puts 'hello world'”<br />> hello world<br />
  17. 17. Now What?<br />
  18. 18. Get the Android SDK<br />Download and install it:<br /><br />Notes on building from the command line: <br /><br />Set up your environment (see above):<br />Make sure to set JAVA_HOME, CLASSPATH, and put your platform-tools directory in your path<br />
  19. 19. Android SDK / Virtual Devices<br />
  20. 20. The anatomy of a typical Android application<br />Activities<br />Intents<br />Manifest File<br />XML Layouts (Views)<br />Services<br />Content Providers<br />(Lots to learn)<br />
  21. 21. Hello Pindah<br />Garrett, Protoform, Mirahndroid => Pindah<br />Goals<br />Make it easy to get started with Android + Mirah<br />Make day to day development tasks easier<br />Provide project structure and conventions<br />Application skeleton generator<br />Rake tasks to hide Ant nastiness<br />Because XML is pain<br />Use Rake to compile / debug / install / etc<br />
  22. 22. What Pindah Does NOT Do<br />No “pretty” wrappers for Android APIs<br />You must learn the APIs to work effectively<br />Does not provide alternatives to XML-based Manifest or view layouts<br /><br />
  23. 23. Get Pindah<br /># For more information, see<br />#<br />$ jruby –S gem installpindah<br /># Generate an Android application skeleton<br />$pindahcreate org.example.hello [/path/to/hello_world] [HelloActivity]<br />
  24. 24. Android App Skeleton<br />├── AndroidManifest.xml<br />├── Rakefile<br />├── libs<br />├── res<br />│   ├── drawable-hdpi<br />│   │   └── ic_launcher.png<br />│   ├── drawable-ldpi<br />│   │   └── ic_launcher.png<br />│   ├── drawable-mdpi<br />│   │   └── ic_launcher.png<br />│   ├── layout<br />│   │   └── main.xml<br />│   └── values<br />│   └── strings.xml<br />└── src<br /> └── org<br /> └── example<br /> └── hello<br /> └── HelloActivity.mirah<br />Managed for you by Pindah:<br /><ul><li>
  25. 25.
  26. 26.
  27. 27. build.xml</li></li></ul><li>Pindah Rake Tasks<br />$ rake -T<br />Android SDK Tools Revision 8<br />Project Target: Android 2.1-update1<br />API level: 7<br />------------------<br />Resolving library dependencies:<br />No library dependencies.<br />------------------<br />Importing rules file: tools/ant/main_rules.xml<br />rake clean # Removes output files created by other targets.<br />rake compile # Compiles project's .mirah files into .class files<br />rake debug # Builds the application and signs it with a debug key.<br />rake install # Installs/reinstalls the debug package onto a running ...<br />rake javac # Compiles and other gen/ files.<br />rake logcat # Tail logs from a device or a device or emulator<br />rake release # Builds the application.<br />rake spec # Print the project spec<br />rake uninstall # Uninstalls the application from a running emulator or dev...<br />
  28. 28. Android Activity Boilerplate<br /># HelloActivity.mirah<br />package org.example.hello<br />import<br />class HelloActivity < Activity<br /> def onCreate(state)<br /> super state<br />setContentViewR.layout.main<br /> end<br />end<br />#<br />package org.example.hello;<br />import;<br />public class HelloActivity<br /> extends Activity<br />{<br /> /** Called when the activity is first created. */<br /> @Override<br /> public void onCreate(<br /> Bundle savedInstanceState)<br /> {<br />super.onCreate(<br />savedInstanceState);<br />setContentView(<br />R.layout.main);<br /> }<br />}<br />
  29. 29. Running the Example App<br />$ cdhello_world<br />$ rake install<br />=> WIN.<br />
  30. 30. Well, that was boring<br />
  31. 31. Slightly More Interesting<br />More expressive code == visible improvement<br />Example application<br />“Up or Down?” website testing app<br /><br />Questions:<br />What do Android callbacks look like?<br />How do I leverage 3rd party (Java) libraries?<br />How do I handle exceptions?<br />How does Mirah deal with scope? (it’s weird)<br />
  32. 32. Up or Down? Down or Up?<br />
  33. 33. Android Listeners (Java)<br />// Sample button click listener in Java<br />// Create an anonymous implementation of OnClickListener<br />private OnClickListenermClickListener = new OnClickListener() {<br /> public void onClick(Viewv) {<br /> // handle click event<br /> }<br />};<br />protected void onCreate(BundlesavedValues) {<br /> ...<br /> // Capture our button from layout<br /> Button mSubmit = (Button)findViewById(;<br /> // Register the onClick listener with theimpl above<br />mSubmit.setOnClickListener(mClickListener);<br /> ...<br />}<br />
  34. 34. StatusActivity + Listeners<br />class StatusActivity < Activity<br /> def onCreate(state)<br /> super state<br />setContentViewR.layout.main<br /> @url = EditTextfindViewById(<br />@submit = Button findViewById(<br />setListeners<br />end<br /> def setListeners<br /> this = self<br /> # SHORTCUT: click listener must implement onClick<br /> @submit.setOnClickListener do |v|<br /> status = this.checkSiteStatus(this.getUrl)<br />this.showResult status<br /> end<br /> end<br />Scoping for ivars and self is incomplete. Assign a local var within scope.<br />Easy to add anonymous listeners with this syntax (implements a single method interface)<br />
  35. 35. Using External Java Libraries<br />Put jars in libs folder and import to use. Simple! (must import Android libs too)<br />import<br />import<br />import org.jsoup.Jsoup<br />import org.jsoup.nodes.Document<br />def checkSiteStatus(address:String):String<br />return "Please specify a URL to test" if address.equals('')<br />begin<br />doc = Jsoup.connect(<br /> "" + address<br /> ).get<br />res ="#container").first.text<br />Log.d 'StatusActivity',<br /> 'Full response from server is: ' + res<br />res.substring(0, res.indexOf('Check another'))<br />rescue SocketTimeoutException => ex<br />"Unable to contact the server. How ironic!”<br />end<br />end<br />Exception handling works like it does in Ruby. Must import specific exceptions.<br />
  36. 36. Wrapping Up: Dialog Example<br />def getUrl<br />@url.getText.toString<br />end<br />def showResult(message:String)<br />alert =<br />alert.setTitle 'Site Test Results’<br />alert.setMessage message<br />alert.setPositiveButton('OK') do |dialog, w|<br />dialog.dismiss<br />end<br /><br />end<br />
  37. 37. Android XML Layouts (main.xml)<br /><?xml version="1.0" encoding="utf-8"?><br /><RelativeLayoutxmlns:android=""<br />android:orientation="vertical"<br />android:layout_width="fill_parent"<br />android:layout_height="fill_parent"><br /> <LinearLayout<br />android:orientation="vertical"<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"><br /> <TextView<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:gravity="center_horizontal"<br />android:textSize="20sp"<br />android:textStyle="bold"<br />android:text="@string/app_title" /><br /> <TextView<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:gravity="center_horizontal"<br />android:textSize="18sp"<br />android:textStyle="bold"<br />android:layout_marginBottom="10dip"<br />android:text="@string/app_subtitle" /><br /> <EditTextandroid:id="@+id/url_txt"<br />android:layout_width="fill_parent"<br />android:singleLine = "true"<br />android:layout_height="wrap_content"<br />android:inputType="textUri"<br />android:hint="@string/url_example" /><br /> <Button android:id="@+id/submit_btn"<br />android:layout_width="140dip"<br />android:layout_marginTop="6dip"<br />android:layout_height="wrap_content"<br />android:layout_gravity="center"<br />android:gravity="center"<br />android:text="@string/submit_btn" /><br /> </LinearLayout><br /> <TextView<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:gravity="center_horizontal"<br />android:layout_alignParentBottom="true"<br />android:autoLink="web"<br />android:text="@string/powered_by" /><br /></RelativeLayout><br /><EditTextandroid:id="@+id/url_txt"<br />android:layout_width="fill_parent"<br />android:singleLine = "true"<br />android:layout_height="wrap_content"<br />android:inputType="textUri"<br />android:hint="@string/url_example" /><br />more info<br />app title<br />[ url input ]<br />[ button]<br />
  38. 38. One More Thing<br />Android Manifest lists top-level activities and required permissions<br />Our app requires Internet access<br />Add the permission to AndroidManifest.xml:<br /><uses-permission android:name="android.permission.INTERNET" /><br />
  39. 39. It Works!<br />
  40. 40. Ideas for Next Steps<br />Implement a ProgressDialog<br />And perform the site check in an AsyncTask<br />Record a log of website test history to a ListView<br />Allow users to browse test history through a separate Activity<br />Store test history to a local Sqlite database (and ListAdapter)<br />Fork me at <br /><br />
  41. 41. Conclusions (or lack thereof)<br />Mirah is a nice midpoint between Ruby and Java<br />Well-suited for Dalvik JVM work<br />But still very immature / not yet practical for daily use<br />Opportunity to help push mobile dev forward<br />Lack of good IDE support<br />Makes working with the (massive) Android API difficult<br />Debugging is a pain in the butt<br />ADB (Rake logcat) is incredibly useful; learn to use it<br />I personally still prefer mobile web development ;-) but sometimes native is the way to go!<br />
  42. 42. And so it goes<br />Mirah Language Resources<br /><br /><br />Pindah and Mirah and Android Oh My!<br /><br /> (experiments)<br /> (urbanspoon)<br /><ul><li>General Android Development
  43. 43.
  44. 44.</li>