Ruboto: Ruby’s and Android’s First Born

The Android platform is Googles attempt to give the iPhone a run for its money. In the last year or two, it grew to be a worthy opponent. One reason for this is the fact that it has a lot of funky goodness to offer: Developers enjoy the possibilities and of course the portability that lets them use their beloved Java libraries with ease since non-native Android applications run on a special Java Virtual Machine or JVM.

JRuby is the award-winning open source software that brings Ruby, our pearl of beauty, on the workhorse that is the JVM which, like I said, is also powering Android applications. To put it in a nutshell, the most natural thing happened and Ruboto was born! Domo arigato indeed.

Since I love both Ruby and Android, I had to test-drive Ruboto, which I did last week. I thought, why not make a short article out of my little adventure? (If you would like to write an article about some cool Ruby stuff yourself, feel free to contact us!)

Let me give you a quick overview of how things come together first and then I’ll show you how to try it yourself.

As you can see, Java source files together with the JRuby JARs are being packed together to an Android application that is able to run your custom Ruby scripts while giving you access to the Android APIs. This is a powerful approach since it enables you to also change the Ruby scripts afterwards without the need to recompile the whole app. The implication here is that you can write Ruby code directly on your device and run it. Admittedly, this is not the most finger friendly way to code, but it is actually nice to have a working IRB on your device to quickly try out something.

How is it Done?

For simplicity and the sake of my mental health I’m only going to show you how I did it on my Linux machine. If you are using Mac or Windows, there are tutorials for you out there too.

System Setup

First you need to have a JDK (Java Development Kit) and Ant (think of Bundler for Java).
sudo apt-get install default-jdk ant

Please verify that you have at least Ant version 1.8.1 or higher. If not, you can get it from here.

Now the Ruboto tutorial installs everything Ruby related via Aptitude, but I tend to favor RVM (the Ruby Version Manager). You can install it simply with bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer) but you should read their install guide first.

After you successfully installed RVM, you can use it to automatically install a Ruby environment for you. I decided to use the latest JRuby version by running rvm install jruby.

Now as you may or may not know, RVM handles your gemsets for your Ruby versions quite transparently, allows us to install the necessary gems with a simple gem install rake bundler ruboto jruby-jars.

The final step is to install the Android SDK. All you have to do for that is download the SDK from here, extract it in a location of your choice (psst, /opt/android-sdk/ is nice!) and add this location to your path variable (e.g. by adding something like
export PATH=${PATH}:/opt/android-sdk/tools to your ~/.bashrc file)

To generate your first Ruboto app, just type ruboto gen app --package org.ruboto.example.quick_start --target android-7 and
cd into the newly created directory quick_start.

Device Setup

I just used my actual Android phone instead of the emulator, but if you prefer using the emulator or if anything went wrong and you need to know more details of the installation process etc, just visit the official Ruboto getting started Tutorial.

In order to use your Android device to try out your app, you need to allow Unknown sources in Settings/Applications and enable USB debugging in Settings/Applications/Development (Settings/Security and Settings/Developer_options since Android 4.0).

One last thing needs to be done before we can install the sample app to the device. You need to create the file /etc/udev/rules.d/51 android.rules by adding the line SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", MODE="0666", GROUP="plugdev" and fire a quick sudo chmod a+r /etc/udev/rules.d/51-android.rules on it.

Please note that you need to exchange the “0bb4″ with your actual vendor ID. The most common ones should be

  • HTC: 0BB4
  • Samsung: 04E8
  • Google: 18D1
  • LG: 1004
  • Motorola: 22B8
  • Huawei: 12D1

The rest can be found here. Again, if you’re using Windows or Mac you will have to do different things. Please go and read these instructions.

The Fun Part

Well, that was easy wasn’t it? Seriously, get a beer or the like if you haven’t already, because the fun part can finally begin: Let’s write some Ruby code! You should now be able to build and install the sample app to your device, assuming it is connected via USB, with a simple rake install.

Since the sample app is really boring, we should make it do something more fun. When I started to play around I thought of writing a little game at first or something with the devices camera but the thing is, this kind project is either totally dumb or too complex to be done in a small article like this one. A friend of mine then told me about a project that he’s working on where he wants to remotely control his fancy TV via Ruby and that made me think. Why not build a small script that connects to a TCP server and sends a message. I liked the idea because it is not much code, itis quickly done, can be easily extended into something potentially useful and I just like network stuff.

The first thing we change is the name of the app in res/values/strings.xml. Right after that, we open the AndroidManifest.xml and just above the </manifest>-tag at the end, we add <uses-permission android:name="android.permission.INTERNET">. Obviously this gives our app the permission to access the network. Now, we can finally
add the Ruby code in src/quick_start_activity.rb. Just replace everything in this file with the following code

Run rake install again to deploy the changes to your device.

What does this code do? After starting the app you should see two text fields and a button. In the first text field we enter a host name of a server in the same network or in the internet. On this machine we should start a TCP Server, which can be done either with the help of the all mighty netcat (nc -l 1234) or with some Ruby code

When we enter a message in the other text field in our app and press the send button we should get the message displayed on the server side. What a great day for science! You know what to do now … go build a laser death ray and control it from your Android device or something like this! (I’m not responsible for any casualties!)

What are your thoughts about JRuby, Android and Ruboto? Just a toy or a game changer?

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Colin Dean

    I’ve been considering using Ruboto, too. How’s the performance? I know early on in the project, its lackluster performance didn’t merit using it instead of straight-up Java except for the purpose of developing quick little tool apps or rapid prototyping.

    • Marc Berszick

      My feeling about the performance so far is that the start up time of the app is a little longer but once it’s loaded it behaves more or less like a native app. Also I’m using a low end device so maybe it’s not even so bad on a more powerful device.

  • Felix

    I think there is a typo that made me wander off in strange dreams for a while:

    “As you can see, Java source files together with the JRuby JARs are being packed together to an Android application that is able to run your custom Ruby scripts while giving you access to the Android APIs.”

    Its __Ruby__ source files, isnt it?

    • Marc Berszick

      You have the Ruby scripts (bottom of the tree) and you have actual Java source files (top of the tree) that build the bridge to the Android system. When you created a sample application look into src/org/ruboto. Hopefully that helps to clarify.

  • Jaume Arús

    Useful article. Thanks.

  • http://www.braindigit.com prem regmi

    I use all of the tuotorial u mention but while i create ruboto gen app –package org.ruboto.example.quick_start –target android-7
    following error occurs help me

    /usr/local/lib/site_ruby/1.9.1/rubygems/dependency.rb:247:in `to_specs’: Could not find ruboto (>= 0) amongst [RedCloth-4.2.9, abstract-1.0.0, actionmailer-3.2.5, actionmailer-3.2.3, actionmailer-3.2.0, actionmailer-3.1.1, actionmailer-3.0.3, actionmailer-3.0.0.beta4, actionmailer-2.3.5, actionmailer-with-request-0.3.0, actionpack-3.2.5, actionpack-3.2.3, actionpack-3.2.0, actionpack-3.1.1, actionpack-3.0.3, actionpack-3.0.0.beta4, actionpack-3.0.0.beta3, actionpack-2.3.5, active_scaffold-3.2.12, active_scaffold_vho-3.1.4, active_utils-1.0.3, activemerchant-1.22.0, activemodel-3.2.5, activemodel-3.2.3, activemodel-3.2.0, activemodel-3.1.1, activemodel-3.0.3, activemodel-3.0.0.beta4, activemodel-3.0.0.beta3, activerecord-3.2.5, activerecord-3.2.3, activerecord-3.2.0, activerecord-3.1.1, activerecord-3.0.3, activerecord-3.0.0.beta4, activerecord-2.3.5, activeresource-3.2.5, activeresource-3.2.3, activeresource-3.2.0, activeresource-3.1.1, activeresource-3.0.3, activeresource-3.0.0.beta4, activeresource-2.3.5, activesupport-3.2.5, activesupport-3.2.3, activesupport-3.2.0, activesupport-3.1.1, activesupport-3.0.3, activesupport-3.0.0.beta4, activesupport-3.0.0.beta3, activesupport-2.3.5, acts_as_indexed-0.7.8, anjlab-bootstrap-rails-0.1.4, ansi-1.3.0, arel-3.0.2, arel-2.2.1, arel-2.0.10, arel-2.0.8, arel-2.0.7, arel-2.0.6, arel-0.4.0, authlogic-3.1.0, awesome_nested_set-2.1.3, awesome_print-0.4.0, aws-s3-0.6.2, babosa-0.3.7, bcrypt-ruby-3.0.1, bcrypt-ruby-2.1.4, blankslate-2.1.2.4, bson-1.6.4, bson-1.5.2, bson_ext-1.6.4, bson_ext-1.5.2, builder-3.0.0, builder-2.1.2, bundler-1.1.3, cancan-1.6.7, capistrano-2.12.0, carrierwave-0.6.2, carrierwave-0.6.1, carrierwave-0.5.1, carrierwave-mongoid-0.2.1, cells-3.8.5, chunky_png-1.2.5, ckeditor-3.7.1, clearance-0.16.3, cloudfactory-0.4.0, cocaine-0.2.1, codemirror-rails-2.24, coderay-1.0.6, coderay-0.9.8, coffee-rails-3.2.2, coffee-rails-3.1.1, coffee-script-2.2.0, coffee-script-source-1.3.3, coffee-script-source-1.3.2, coffee-script-source-1.3.1, coffee-script-source-1.1.2, columnize-0.3.2, commonjs-0.2.6, compass-0.12.2, compass-rails-1.0.3, configuration-1.2.0, countries-0.8.2, country_select-1.0.0, currencies-0.4.0, custom_fields-2.0.0.rc12, daemons-1.1.4, devise-2.1.2, devise-2.0.4, devise-1.1.9, devise-encryptable-0.1.1, diesel-0.1.5, dragonfly-0.9.12, ejs-1.0.0, erubis-2.7.0, erubis-2.6.6, eventmachine-0.12.10, excon-0.13.4, execjs-1.4.0, execjs-1.3.2, execjs-1.2.9, faraday-0.8.1, fastercsv-1.5.4, flash_cookie_session-1.1.3, fog-1.3.1, formatador-0.2.3, formatador-0.2.1, formtastic-2.0.2, friendly_id-4.0.7, fssm-0.2.9, galetahub-simple_captcha-0.1.3, globalize3-0.2.0, gmaps4rails-1.5.2, haml-3.1.6, haml_coffee_assets-0.9.4, hashie-1.1.0, heroku-1.17.10, highline-1.6.13, highline-1.6.12, highline-1.6.2, hike-1.2.1, httparty-0.8.3, httparty-0.8.1, i18n-0.6.0, i18n-0.5.0, i18n-0.4.2, i18n-0.3.7, i18n-js-2.1.2, jbuilder-0.4.0, jnunemaker-validatable-1.8.4, joosy-1.0.0.RC2, journey-1.0.4, journey-1.0.3, jquery-rails-2.0.2, jquery-rails-1.0.19, jquery-rails-1.0.14, jquery-rails-0.2.7, jquery-rails-0.2.6, jruby-pageant-1.0.2, json-1.7.3, json-1.7.1, json-1.6.6, json-1.6.1, json-1.4.6, kaminari-0.13.0, kaminari-0.10.1, kgio-2.7.4, launchy-0.3.7, less-2.2.1, less-rails-2.2.3, libv8-3.3.10.4-x86_64-linux, locomotive-aloha-rails-0.20.1.4, locomotive-mongoid-tree-0.6.2, locomotive-tinymce-rails-3.4.7.2, locomotive_cms-2.0.0.rc9, locomotive_liquid-2.2.2, mail-2.4.4, mail-2.3.0, mail-2.2.19, mail-2.2.15, mail-2.2.12, mail-2.2.7, memcache-client-1.8.5, metaclass-0.0.1, method_source-0.6.6, millisami-csv-hash-0.2.0, mime-types-1.19, mime-types-1.18, mime-types-1.16, mimetype-fu-0.1.2, mini_exiftool-1.4.2, mini_magick-3.4, mocha-0.11.4, mocha-0.9.10, money-5.0.0, mongo-1.6.4, mongo-1.5.2, mongo_mapper-0.11.1, mongo_mapper-0.8.6, mongoid-3.0.1, mongoid-2.4.11, moped-1.1.2, multi_json-1.3.6, multi_json-1.3.5, multi_json-1.3.4, multi_json-1.3.2, multi_json-1.0.3, multi_xml-0.5.1, multi_xml-0.4.1, multipart-post-1.1.5, mysql-2.8.1, mysql2-0.3.11, negative_captcha-0.3, negative_captcha-0.2.beta, net-ldap-0.3.1, net-scp-1.0.4, net-sftp-2.0.5, net-ssh-2.5.2, net-ssh-2.4.0, net-ssh-2.3.0, net-ssh-gateway-1.1.0, nifty-generators-0.4.6, nifty-generators-0.4.4, nifty-generators-0.4.2, nokogiri-1.5.5, nokogiri-1.5.2, oauth-0.4.6, oauth2-0.5.2, origin-1.0.4, orm_adapter-0.4.0, orm_adapter-0.3.0, orm_adapter-0.0.7, paper_trail-2.6.3, paperclip-3.1.2, paperclip-2.5.2, pg-0.14.0, pg-0.13.2, pg-0.11.0, pjax_rails-0.1.10, plucky-0.4.4, plucky-0.3.8, polyglot-0.3.3, polyglot-0.3.2, polyglot-0.3.1, prem-0.0.0, prototype-rails-3.2.1, pry-0.9.6.2, pry-doc-0.3.0, rack-1.4.1, rack-1.3.4, rack-1.2.5, rack-1.2.1, rack-1.1.3, rack-1.0.1, rack-cache-1.2, rack-cache-1.1, rack-mount-0.8.3, rack-mount-0.6.14, rack-mount-0.6.13, rack-openid-1.3.1, rack-ssl-1.3.2, rack-test-0.6.1, rack-test-0.5.7, rack-test-0.5.6, rails-3.2.5, rails-3.2.3, rails-3.2.0, rails-3.1.1, rails-3.0.3, rails-3.0.0.beta4, rails-2.3.5, rails-backbone-0.6.1, rails3-generators-0.14.0, railties-3.2.5, railties-3.2.3, railties-3.2.0, railties-3.1.1, railties-3.0.3, railties-3.0.0.beta4, raindrops-0.10.0, rake-0.9.2.2, rake-0.9.2, rake-0.8.7, rdoc-3.12, rdoc-3.10, refinerycms-2.0.5, refinerycms-authentication-2.0.5, refinerycms-core-2.0.5, refinerycms-dashboard-2.0.5, refinerycms-i18n-2.0.0, refinerycms-images-2.0.5, refinerycms-pages-2.0.5, refinerycms-resources-2.0.5, render_component_vho-3.2.1, responders-0.6.5, responders-0.6.4, rest-client-1.6.7, rest-client-1.6.1, rmagick-2.13.1, rmagick-2.12.2, routing-filter-0.3.1, ruby-hmac-0.4.0, ruby-openid-2.1.8, ruby_parser-2.0.6, rubygems-bundler-0.9.2, rvm-1.11.3.3, sanitize-2.0.3, sass-3.1.20, sass-3.1.19, sass-3.1.18, sass-3.1.17, sass-3.1.10, sass-rails-3.2.5, sass-rails-3.1.4, seo_meta-1.3.0, sexp_processor-3.0.7, shoulda-2.11.3, slop-2.1.0, sorcery-0.7.12, sprockets-2.1.3, sprockets-2.1.2, sprockets-2.0.2, sqlite3-1.3.6, sqlite3-ruby-1.2.5, subexec-0.2.2, terminal-table-1.4.2, therubyracer-0.10.1, thin-1.2.11, thor-0.15.4, thor-0.15.3, thor-0.15.2, thor-0.14.6, thor-0.13.8, tilt-1.3.3, treetop-1.4.10, treetop-1.4.9, truncate_html-0.5.5, turn-0.8.3, twitter-bootstrap-rails-2.0.9, twitter_bootstrap_form_for-1.0.1, tzinfo-0.3.33, tzinfo-0.3.30, tzinfo-0.3.24, tzinfo-0.3.23, uglifier-1.2.6, uglifier-1.2.5, uglifier-1.2.4, uglifier-1.0.3, unicorn-4.3.1, unidecoder-1.1.1, warden-1.2.1, warden-1.1.1, warden-1.0.6, will_paginate-3.0.3, xml-simple-1.1.1, yard-0.7.2] (Gem::LoadError)
    from /usr/local/lib/site_ruby/1.9.1/rubygems/dependency.rb:256:in `to_spec’
    from /usr/local/lib/site_ruby/1.9.1/rubygems.rb:1231:in `gem’
    from /usr/bin/ruboto:18:in `’