RubyMotion Runtime Guide for Android

The RubyMotion runtimes implement the Ruby language functionality required during the execution of an application. The object model, built-in classes, and memory management system are part of the runtime.

RubyMotion comes with a runtime for Android, based on the Android runtime (Dalvik or ART) and core Java classes.

Although similar in appearance, the RubyMotion Android runtime has completely different implementations than CRuby, the mainstream implementation (which is also called MRI, or simply Ruby). We will cover the main differences in the following sections.

The key feature of the RubyMotion Android runtime is its tight integration with the Java virtual machine, which makes it suitable for use on power efficient devices as well as for writing performance-intensive code.

RubyMotion follows the Ruby 1.9 language specifications.

1. Object Model

The RubyMotion object model is based on Java, the de-facto programming language of Android, Google’s mobile platform.

While Java has certainly less similarities with Ruby than Objective-C, they are still close enough to make such an integration possible.

More specifically, RubyMotion re-implements the Ruby language on top of the Java Native Interface (JNI) of the Android runtime.

Android RubyMotion Runtime

In RubyMotion, all objects are Java objects, all Java classes and methods are available in Ruby, and some Ruby classes and methods are available to Java. This unified approach allows us to interface with the native Java APIs at no extra performance cost.

RubyMotion is compatible with both Dalvik and ART Android runtimes.

1.1. Classes

All Java classes are natively exposed to Ruby. However, the way to access them differs from the syntax you would use in Java.

Java features the notion of packages, which is a way to group a set of classes under a component that can be either accessed directly or included.

As an example, java.lang.Object represents a path where the Object class is defined under the java.lang package. Java classes are often described using their full package path.

In RubyMotion, Java packages are accessed as if they were defined as a chain of Ruby constants, where each part of the package starts with an upper-case character.

In Ruby, the java.lang.Object path can be accessed using Java::Lang::Object.

obj = Java::Lang::Object.new

Java classes which are not defined as final can be subclassed. As an example, the public non-final android.app.Activity class is available as Android::App::Activity in Ruby, and can be subclasses as following.

class MyActivity < Android::App::Activity
  # ...
end
Note
At the time of this writing, packages cannot be included in Ruby, so you have to use full paths.

1.2. Primitive Java Types

Like Objective-C, Java comes with a set of primitive / builtin data types. Java methods make use of these types, and it is important to describe how they are interfaced with Ruby code.

Java Type From Ruby to Java From Java to Ruby

boolean

If the object is false or nil, false, otherwise, true. Note: the 0 fixnum will evaluate to true.

Either true or false.

byte

If the object is a Fixnum or a Bignum, the value is returned. If the object is true or false, 1 or 0 are respectively returned. If the object responds to the to_i message, it is sent and the result is returned.

Either a Fixnum or a Bignum object.

char

short

int

long

float

If the object is a Float, the value is returned. If the object is true or false, 1.0 or 0.0 are respectively returned. If the object responds to the to_f message, it is sent and the result is returned.

A Float object.

double

1.3. Builtin Ruby Classes

Some of the built-in classes of RubyMotion are based on the Java Class Library, the core classes of the Android SDK.

Java comes with a class called java.lang.Object, which is the root class of all Java classes.

Because of RubyMotion’s unified runtime approach, all Ruby classes are also based on java.lang.Object. The Object constant point to that class as well.

Here is a table showing the base class of a newly created class, Hello, as well as some of the Ruby built-in classes.

Ruby Class Base

Hello

java.lang.Object

Fixnum

java.lang.Integer

Float

java.lang.Float

Proc

java.lang.Runnable (interface)

String

java.lang.CharSequence (interface)

Array

java.util.ArrayList

Hash

java.util.HashMap

Thread

java.lang.Thread

Regexp

java.util.regex.Pattern

MatchData

java.util.regex.Matcher

The main purpose of this design is to allow the exchange of primitive types between Java and Ruby at no performance cost, since they don’t have to be converted. This is important as most of the types that will be exchanged in a typical application are likely going to be built-in types.

A String object created in Ruby can have its object reference passed as the argument of an Java method that expects an object that implements the java.lang.CharSequence interface, such as the setText method of android.widget.TextView class.

textView.text = "foo"

1.4. Methods

Java methods are natively exposed to Ruby, and can be accessed as if they have been defined as Ruby methods.

As an example, the java.lang.Object class implements the toString() method.

String	toString()

That method can be called on all objects in Ruby.

42.toString         #=> "42"
true.toString       #=> "true"
textView.toString   #=> "android.widget.TextView{3607..."

When you are subclassing a Java class in Ruby, you can override any of its methods.

As an example, the android.app.Activity class implements the onCreate() method, which is meant to be overridden by subclasses.

void onCreate (Bundle savedInstanceState)

We can do that naturally from Ruby.

class MyActivity < Android::App::Activity
  def onCreate(saved_instance)
    super
    # ...
  end
end

Note that we use the super Ruby language keyword to call the super class implementation, defined in Java.

1.5. Method Shortcuts

The RubyMotion runtime provides convenience shortcuts for certain Java methods.

Method Shortcut

setFoo

foo=

getFoo

foo

isFoo

foo?

As an example, the getText and setText methods of android.widget.TextView can be called using the text and text= shortcuts.

view.text          # instead of view.getText
view.text = "foo"  # instead of view.setText("foo")

2. Interfacing with Java

2.1. Interfaces

Java interfaces are an important concept to understand when programming Android apps with RubyMotion.

In Java, an interface is basically a list of method declarations. A class is said to implement an interface when it provides definitions for all the methods in the interface.

Note
Java interfaces are similar to protocols in Objective-C.

In RubyMotion, you can implement an interface in a class just by defining the required methods. There is no need to specify the name of the interface, the compiler will determine that for you automatically.

As an example, the setOnClickListener method of android.view.View expects an object that implements the android.view.View.OnClickListener interface, which features only one method, onClick.

abstract void	onClick(View v)

In order to implement that interface in Ruby, you just need to provide an implementation for that method in a class.

class MyButtonListener
  def onClick(view)
    # ...
  end
end

# ...
button.onClickListener = MyButtonListener.new

2.2. Methods Overloading

Java features the concept of method overloading, which does not exist in Ruby.

In Java, it is possible to define the same method name multiple times but with different argument types. The compiler will then identify which method has to be called.

As an example, the android.widget.TextView Java class features the following setText methods:

void	setText(int resid)
void	setText(char[] text, int start, int len)
void	setText(int resid, TextView.BufferType type)
void	setText(CharSequence text)
void	setText(CharSequence text, TextView.BufferType type)

As you can see, each of these methods have the same name (and return type) but have different arguments.

Because Ruby is dynamically-typed, overloaded methods have to be resolved at runtime and not at compile time.

The setText(int resid) method will be called if you pass a Fixnum to it. The setText(CharSequence text) method will be called if you provide an object that implements the CharSequence interface (such as a string).

textView.text = 42      #=> calls setText(int)
textView.text = "foo"   #=> calls setText(CharSequence)

In case the runtime cannot figure out which overloaded method to call during a method dispatch, a NoMethodError exception will be raised with an appropriate message.

2.3. Fields

Java fields are variables attached to a class. There are two types of Java fields:

  1. Instance fields; these are similar to instance variables in Ruby.

  2. Static fields; these are similar to class variables in Ruby.

In the Android SDK, constants are usually static Java fields, and can be retrieved exactly as if they were constants in Ruby.

For example, the android/widget/LinearLayout class has a few constants.

public static final int HORIZONTAL
public static final int VERTICAL
...

These constants can be naturally retrieved in Ruby.

layout = Android::Widget::LinearLayout.new(self)
layout.orientation = Android::Widget::LinearLayout::VERTICAL

Instance Java fields can be retrieved in Ruby as if they were defined using attr_accessor.

2.4. Annotations

Java annotations are special objects that can be applied to methods (and other types).

An example is the WebKit framework of Android, which requires methods to be exposed to JavaScript to be properly annotated, for security reasons.

This can be done in RubyMotion using the annotation special method right before the definition of a method. The annotation will be retrieved at compilation time.

class DemoJavaScriptInterface
  __annotation__("@android.webkit.JavascriptInterface")
  def methodFromJavaScript
  end
end
Note
At the time of this writing, only methods can be annotated in RubyMotion.

3. Memory Management

RubyMotion relies on the Dalvik or ART garbage collector to allocate and dispose of unused memory.

From a Ruby developer perspective, you do not have to think about memory management at all.

Internally, RubyMotion makes use of local references for local variables and global references for everything else. The destruction of local references is determined at compilation time.

There are two important things you might need to consider:

  1. The garbage collector is multithreaded. Collections might happen on other threads. If you override the finalize method of java.lang.Object in your code, make sure it’s properly re-entrant.

  2. The garbage collector is able to detect and handle cyclic references. Unlike RubyMotion for iOS and OS X, you do not need to create WeakRef objects.

4. Concurrency

RubyMotion has been designed for concurrency in mind.

RubyMotion has the concept of virtual machine objects, which wrap the state of a thread of execution. A piece of code is running through a virtual machine.

Virtual machines don’t have locks and there can be multiple virtual machines running at the same time, concurrently.

Caution
Unlike the mainstream Ruby implementation, race conditions are possible in RubyMotion, since there is no Global Interpreter Lock (GIL) to prohibit threads from running concurrently. You must be careful to secure concurrent access to shared resources.

The Thread class is available for concurrency patterns. It is a direct subclass of java.lang.Thread.

 

Can we be of any help?

Check our consulting services