Building native Android applications with Mirah and Pindah

Preview:

DESCRIPTION

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

Citation preview

So you want to build Android apps… without Java?

Nick Plante @zapnap

(A brief introduction to Mirah and Pindah)

Who?

Nick Plante (not a Java developer)

Zerosum Labs Rails Rumble Rubydoc.info

Chinaccelerator

Contact me! @zapnap on Twitter http://github.com/zapnap

Why?

Because you’re a Ruby developer (familiarity).

Because Android is awesome. Because simplicity is elegance.

“Think Different.”

Java vs Ruby

Dynamic vs Static Simplicity vs Complexity & Verbosity Less Ceremony

Java is a systems programming language, after all

But… The JVM is an awesome platform Android’s use of the JVM (Dalvik) gives

us options

Java Alternatives

Android Scripting Environment (SL4A) Great for scripting; not great for applications Limited access to Android API / GUI Performance issues

Other JVM-based languages: Scala Clojure JRuby Mirah Groovy?

JRuby & Mr Ruboto

Ruboto is your friend! JRuby interface / framework for Android

APIs http://ruboto.org

But JRuby runtime overhead is a problem Slow startup (~10 seconds) Large APK size▪ HelloWorld: 3.4MB compressed, 10MB installed

Introducing Mirah

Mirah compiles straight to Java bytecode Very fast, no extra overhead

Syntax is very Ruby-ish Statically-typed with local type inference “Ruby with type annotations”

No runtime library Mix and match Java code

Warning!

Mirah is still a very young language (v0.0.7)

Tooling is very, very alpha Advantage: Eclipse (Java) Redcar looks promising

Compilation errors are very, very not-so-funNativeException: jmeta.SyntaxError: expected Ensure before ' { |a b| Inte' (at line: 16, char: 40)

One thing at a time

We’ll get to Android in just a second First let’s see some basic Mirah

syntax…

# fib.mirahdef fib(a:int):int if a < 2 a else fib(a-1) + fib(a-2) endend

puts fib(20)

Ruby vs Mirah

# fib.rubydef fib(a) if a < 2 a else fib(a-1) + fib(a-2) endend

puts fib(20)

Parameter type declaration???SRSLY?

Return type can often be inferred.

.class output

Produces a java .class $ mirahc fib.mirah public static int fib(int)

Can also produce .java code $ mirahc -j fib.mirah * I have no idea why you would want to

do this.

.to_java => “ick”

// Generated from hello.mirahpublic class Hello extends java.lang.Object { public static void main(java.lang.String[] argv) { java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println(Hello.fib(20)); } public static int fib(int a) { return (a < 2) ? (a) : ((Hello.fib((a - 1)) + Hello.fib((a - 2)))); }}

Challenges / Major Differences Java stdlib: both a blessing and a curse

List, HashMap, etc▪ arr.get(1) vs arr[0]

Blocks work, but syntactic sugar required For example, List#each can be used (array) But HashMap#each does not exist

No optional arguments, no *splats, no ranges

Using Java’s Standard Libraryimport java.util.Collectionsimport java.util.HashMapimport java.util.List

class ListPrinter def print(list:List) puts "first item: #{list.get(0)}" list.each do |item| puts "item: #{item}" end end end

class HashPrinter def print(map:HashMap) map.keySet.each do |key| puts "#{key}: #{map.get(key)}" end end end

map = { 'batman' => 'bruce wayne', 'superman' => 'clark kent' }HashPrinter.new.print(map)

list = ['peter', 'stewie', 'brian']ListPrinter.new.print(list)

Get Mirah

# Install JRuby 1.6 if you haven’t already# (or rvm use jruby)

$ jruby –S gem install mirah

$ mirah -e "puts 'hello world'”> hello world

Now What?

Get the Android SDK

Download and install it: http://developer.android.com/sdk/index.html

Notes on building from the command line: http://developer.android.com/guide/developing/

projects/projects-cmdline.html

Set up your environment (see above): Make sure to set JAVA_HOME, CLASSPATH, and

put your platform-tools directory in your path

Android SDK / Virtual Devices

The anatomy of a typical Android application Activities Intents Manifest File XML Layouts (Views) Services Content Providers

(Lots to learn)

Hello Pindah

Garrett, Protoform, Mirahndroid => Pindah

Goals Make it easy to get started with Android + Mirah Make day to day development tasks easier Provide project structure and conventions

Application skeleton generator Rake tasks to hide Ant nastiness

Because XML is pain Use Rake to compile / debug / install / etc

What Pindah Does NOT Do

No “pretty” wrappers for Android APIs You must learn the APIs to work

effectively

Does not provide alternatives to XML-based Manifest or view layouts https://github.com/objo/droid-views

Get Pindah

# For more information, see# http://github.com/mirah/pindah$ jruby –S gem install pindah

# Generate an Android application skeleton$ pindah create org.example.hello

[/path/to/hello_world] [HelloActivity]

Android App Skeleton

├── AndroidManifest.xml├── Rakefile├── libs├── res│   ├── drawable-hdpi│   │   └── ic_launcher.png│   ├── drawable-ldpi│   │   └── ic_launcher.png│   ├── drawable-mdpi│   │   └── ic_launcher.png│   ├── layout│   │   └── main.xml│   └── values│   └── strings.xml└── src └── org └── example └── hello └── HelloActivity.mirah

Managed for you by Pindah:

• default.properties• build.properties• local.properties• build.xml

Pindah Rake Tasks

$ rake -TAndroid SDK Tools Revision 8Project Target: Android 2.1-update1API level: 7

------------------Resolving library dependencies:No library dependencies.

------------------

Importing rules file: tools/ant/main_rules.xmlrake clean # Removes output files created by other targets.rake compile # Compiles project's .mirah files into .class filesrake debug # Builds the application and signs it with a debug key.rake install # Installs/reinstalls the debug package onto a running ...rake javac # Compiles R.java and other gen/ files.rake logcat # Tail logs from a device or a device or emulatorrake release # Builds the application.rake spec # Print the project specrake uninstall # Uninstalls the application from a running emulator or dev...

Android Activity Boilerplate

# HelloActivity.mirahpackage org.example.hello

import android.app.Activity

class HelloActivity < Activity def onCreate(state) super state setContentView R.layout.main endend

# HelloActivity.javapackage org.example.hello;

import android.app.Activity;

public class HelloActivity extends Activity{ /** Called when the activity is first

created. */ @Override public void onCreate( Bundle savedInstanceState) { super.onCreate( savedInstanceState); setContentView( R.layout.main); }}

Running the Example App$ cd hello_world$ rake install=> WIN.

Well, that was boring

Slightly More Interesting

More expressive code == visible improvement

Example application “Up or Down?” website testing app http://github.com/zapnap/upordown

Questions: What do Android callbacks look like? How do I leverage 3rd party (Java) libraries? How do I handle exceptions? How does Mirah deal with scope? (it’s weird)

Up or Down? Down or Up?

Android Listeners (Java)

// Sample button click listener in Java

// Create an anonymous implementation of OnClickListenerprivate OnClickListener mClickListener = new OnClickListener() { public void onClick(View v) { // handle click event }};

protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button mSubmit = (Button)findViewById(R.id.submit_btn); // Register the onClick listener with the impl above mSubmit.setOnClickListener(mClickListener); ...}

StatusActivity + Listenersclass StatusActivity < Activity def onCreate(state) super state setContentView R.layout.main @url = EditText findViewById(R.id.url_txt) @submit = Button findViewById(R.id.submit_btn)

setListeners end

def setListeners this = self # SHORTCUT: click listener must implement onClick @submit.setOnClickListener do |v| status = this.checkSiteStatus(this.getUrl) this.showResult status end end

Scoping for ivars and self is incomplete. Assign a local var within scope.

Easy to add anonymous listeners with this syntax (implements a single method interface)

Using External Java LibrariesPut jars in libs folder and import to use. Simple! (must import Android libs too)

import java.net.URLimport java.net.SocketTimeoutException

import org.jsoup.Jsoupimport org.jsoup.nodes.Document

def checkSiteStatus(address:String):String return "Please specify a URL to test" if address.equals('')

begin doc = Jsoup.connect( "http://downforeveryoneorjustme.com/" + address ).get res = doc.select("#container").first.text

Log.d 'StatusActivity', 'Full response from server is: ' + res res.substring(0, res.indexOf('Check another')) rescue SocketTimeoutException => ex "Unable to contact the server. How ironic!” endend

Exception handling works like it does in Ruby. Must import specific exceptions.

Wrapping Up: Dialog Example

def getUrl @url.getText.toStringend

def showResult(message:String) alert = AlertDialog.Builder.new(self) alert.setTitle 'Site Test Results’ alert.setMessage message alert.setPositiveButton('OK') do |dialog, w| dialog.dismiss end

alert.showend

Android XML Layouts (main.xml)

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:textSize="20sp" android:textStyle="bold" android:text="@string/app_title" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:textSize="18sp" android:textStyle="bold" android:layout_marginBottom="10dip" android:text="@string/app_subtitle" /> <EditText android:id="@+id/url_txt" android:layout_width="fill_parent" android:singleLine = "true" android:layout_height="wrap_content" android:inputType="textUri" android:hint="@string/url_example" /> <Button android:id="@+id/submit_btn" android:layout_width="140dip" android:layout_marginTop="6dip" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:text="@string/submit_btn" /> </LinearLayout> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:layout_alignParentBottom="true" android:autoLink="web" android:text="@string/powered_by" /></RelativeLayout>

<EditText android:id="@+id/url_txt" android:layout_width="fill_parent" android:singleLine = "true" android:layout_height="wrap_content" android:inputType="textUri" android:hint="@string/url_example" />

more info

app title[ url input ][ button]

One More Thing

Android Manifest lists top-level activities and required permissions

Our app requires Internet access Add the permission to

AndroidManifest.xml: <uses-permission android:name="android.permission.INTERNET" />

It Works!

Ideas for Next Steps

Implement a ProgressDialog And perform the site check in an AsyncTask

Record a log of website test history to a ListView Allow users to browse test history through a

separate Activity Store test history to a local Sqlite database

(and ListAdapter)

Fork me at http://github.com/zapnap/upordown

Conclusions (or lack thereof) Mirah is a nice midpoint between Ruby and Java

Well-suited for Dalvik JVM work But still very immature / not yet practical for daily use Opportunity to help push mobile dev forward

Lack of good IDE support Makes working with the (massive) Android API difficult

Debugging is a pain in the butt ADB (Rake logcat) is incredibly useful; learn to use it

I personally still prefer mobile web development ;-) but sometimes native is the way to go!

And so it goes

Mirah Language Resources http://mirah.org http://github.com/mirah/mirah

Pindah and Mirah and Android Oh My! http://github.com/mirah/pindah http://github.com/technomancy/garrett (experiments) http://threebrothers.org/brendan (urbanspoon)

General Android Development http://developer.android.com/sdk/index.html http

://developer.android.com/guide/developing/projects/projects-cmdline.html

Recommended