Building native Android applications with Mirah and Pindah

Post on 10-May-2015

8804 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

Transcript

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

top related