Dynamic Languages on Android
Mikael KindborgMoSync AB
mikael.kindborg@mosync.com
http://divineprogrammer.se
Early bound vs. Late bound
When you
learn this...
----------------------------------------!------>
^
Can you go back
and change this?
The answer is "Yes", if you late bind...Outline of talk
Dynamic languages on AndroidJavaScript in Rhino
DroidScript
JavaScript in WebKit
MobileLua and MoSync
Hobbyist programming - making app development easy & fun
Why use dynamic Languages?
Incremental development - no build processLess code, less syntactic noise
Dynamic typing and polymorphism - refactoring friendly
Higher level programming constructs
Dynamic languages on Android
Scripting Layer for Android (SL4A) - Python, Lua, JavaScript, Ruby, Perl, BeanShell, ...Interpreter embedded in a native Java app - JavaScript (Rhino), JRuby, BeanShell, Kawa, Clojure, ...
JavaScript running in WebKit
Stand-alone authoring tool/engine - Flash, Corona, AppInventor, Squeak, MobileLua, ...
Mozilla Rhino
JavaScript system written in JavaHas both interpreter/runtime engine and bytecode compiler
Mature project
Moderate footprint (DroidScript is around 360KB)
Good performance
DroidScript
JavaScript on AndroidMicke's project for learning Android
Experimental open-source project (MIT)
Based on Mozilla Rhino JavaScript engine
Full access to the Android API (minus subclassing)
Remote browser-based editor built with CodeMirror
Goal: Interactive programming, developing Android applications without a heavyweight IDE
Code example - display a Toast
var Toast = Packages.android.widget.Toast;
Toast.makeText(
Activity,
"Hello World!",
Toast.LENGTH_SHORT).show();Ways DroidScript can be used
Write code in the on-device editor (works crappy).Load/Run JavaScript files from the SD card or over the Internet.
Load/Run JavaScript code embedded on a web page within DROIDSCRIPT_BEGIN and DROIDSCRIPT_END tags - this means that programs can be published on a blog page, for example.
Do remote programming via the built-in web server that accepts PUT requests with JavaScript code.
Build a native app that embeds JavaScript files as assets.
JavaScript advantages
Late bindingReduced syntactic noise
High-level language constructs
Closures
Compact event callback functions
Flexible object model (prototypes)
More of a language for everyone compared to Java
JavaScript is the new 'machine language'; old and new languages compile to JavaScript, e.g. CoffeeScript
JavaScript drawbacks
Poor tool support (editor, debugger)Almost no static code checking - lots of runtime errors
No dynamic byte code generation
Scripting Java from Rhino - what works and what doesn't
Can use Java packagesCan instantiate Java classes
Can call methods and access instance variables
Data types are automatically converted between Java and JavaScript
Can instantiate interfaces that have one method (similar to an anonymous inner class in Java, but shorther syntax)
Must code a bit to implement multi-method interfaces
Cannot generate Dalvik byte code on the fly
Cannot subclass
Must run in interpreted mode
Android resource XML is early bound; some classes need resource ids, e.g. ArrayAdapter, means you cannot dynamically specify views as list items.
Code example - making a clickable button
// Short names for packages.
var widget = Packages.android.widget;
var graphics = Packages.android.graphics;
// onCreate is called when the script has been loaded.
function onCreate(bundle)
{
var font = graphics.Typeface.create(
graphics.Typeface.SANS_SERIF,
graphics.Typeface.BOLD);
// Activity is a global variable that refers
// to the Android activity.
var button = new widget.Button(Activity);
button.setText("Hello World!");
button.setTypeface(font);
button.setTextSize(26);
button.setBackgroundColor(graphics.Color.rgb(0, 0, 64));
button.setTextColor(graphics.Color.rgb(255, 255, 255));
// One method interface implemented as a function.
button.setOnClickListener(function() {
button.setText("You Clicked Me!"); });
Activity.setContentView(button);
}Refering to packages and classes
// Packages is a global variable that contains // all packages and classes."), // Write out full package path. var button = new Packages.android.widget.Button(Activity);"), // Use variable to refer to a package. var widget = Packages.android.widget; var button = new widget.Button(Activity); // Use variable to refer to a class. var Button = Packages.android.widget.Button; var button = new Button(Activity);
Interfaces, closures, and polymorphism
// You implement a single method interface as a function.
// Example interface: View.OnClickListener.onClick(View v)
// Variable button is contained in the function closure.
// Method parameters need not to be spelled out.
button.setOnClickListener(function() {
button.setText("You Clicked Me!"); });
// Using the supplied parameter, note the use of
// dynamic typing and polymorphism.
button.setOnClickListener(function(view) {
view.setText("You Clicked Me!"); });
// First example in Java, variable button must be final.
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
button.setText("You Clicked Me!"); } });
// Second example in Java, you must type cast.
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
((Button)view).setText("You Clicked Me!"); } });Code example - a counting button
var widget = Packages.android.widget;
var graphics = Packages.android.graphics;
function onCreate(bundle)
{
var font = graphics.Typeface.create(
graphics.Typeface.SANS_SERIF,
graphics.Typeface.BOLD);
var button = new widget.Button(Activity);
button.setText("Hello World!");
button.setTypeface(font);
button.setTextSize(26);
button.setBackgroundColor(graphics.Color.rgb(0, 0, 64));
button.setTextColor(graphics.Color.rgb(255, 255, 255));
var counter = 0;
button.setOnClickListener(function() {
counter++;
button.setText("Click count: " + counter); });
Activity.setContentView(button);
}How to implement multi-method interfaces
// Program that displays images from the camera.
var Camera = Packages.android.hardware.Camera;
var SurfaceHolder = Packages.android.view.SurfaceHolder;
var SurfaceView = Packages.android.view.SurfaceView;
var Window = Packages.android.view.Window;
function onCreate(bundle)
{
Activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
var preview = createPreviewSurface();
Activity.setContentView(preview.getSurfaceView());
}
function createPreviewSurface()
{
var camera = null;
var surface = new SurfaceView(Activity);
// JavaScript object that implements the interface
// android.view.SurfaceHolder.Callback
var object = {
getSurfaceView: function() {
return surface; },
surfaceCreated: function(holder) {
camera = Camera.open();
try {
camera.setPreviewDisplay(holder); }
catch (exception) {
camera.release();
camera = null; } },
surfaceDestroyed: function(holder) {
camera.stopPreview();
camera.release();
camera = null; },
surfaceChanged: function(holder, format, w, h) {
var parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
// Causes camera to fail on Nexus One
//camera.setParameters(parameters);
camera.startPreview(); }
};
// Create the instance of the interface.
var callback = createInstance(
// Must use full package name for some reason...
Packages.android.view.SurfaceHolder.Callback,
object);
// Set the callback for the surface.
surface.getHolder().addCallback(callback);
surface.getHolder().setType(
SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
return object;
}
// Create an instance of a Java interface.
// javaInterface - the interface type
// object - JS object that will receive messages
// sent to the instance
function createInstance(javaInterface, object)
{
// Local function to convert a Java array to
// a JavaScript array
function javaArrayToJsArray(javaArray) {
var jsArray = [];
for (var i = 0; i < javaArray.length; ++i) {
jsArray[i] = javaArray[i]; }
return jsArray; }
// Short name for lang package.
var lang = Packages.java.lang;
// Interface we implement.
var interfaces =
lang.reflect.Array.newInstance(lang.Class, 1);
interfaces[0] = javaInterface;
// Create proxy object.
var obj = lang.reflect.Proxy.newProxyInstance(
lang.ClassLoader.getSystemClassLoader(),
interfaces,
// This function is called on method invocation.
// Note that args is a Java array.
function(proxy, method, args) {
// Apply JavaScript function to arguments.
return object[method.getName()].apply(
null,
javaArrayToJsArray(args)); });
return obj;
}DroidScript architecture
DroidScriptActivity.java - basic Android activity that embeds the Rhino engineDroidScriptApp.java - extends DroidScriptActivity, provides the starting point of the application
DroidScriptApp.js - The actual code for the DroidScript application, all of the user interface and application logic is here
DroidScriptServer.java - tiny web server that can be used from JavaScript
DroidScriptServer.js - The user interface and logic for the server activity
Limitation: require is currently not implemented, but you can load JavaScript code into the global scope
Event handling functions (called from DroidScriptActivity.java)
onCreateonStart
onRestart
onResume
onPause
onStop
onDestroy
onCreateContextMenu
onContextItemSelected
onCreateContextMenu
onCreateOptionsMenu
onPrepareOptionsMenu
onOptionsItemSelected
Evaluating JavaScript (methods in DroidScriptActivity.java)
eval(String code)evalFileOrUrl(String fileNameOrUrl)
evalAssetFile(String fileName)
// These can be called from JavScript, for example:
Activity.evalFileOrUrl("http://www.mydomain.se/MyCode.js");Or use eval in JavaScriptHow to build a native JavaScript app
DroidScriptActivity.java - use as isMyApp.java - extend DroidScriptActivity, and load your JavaScript code from asset files, or download over the Internet and optionally cache files on the device
MyApp.js - the JavaScript code of the application, place in the assets folder in the Eclipse project
// MyApp.java - example of a stand alone DroidScript app.
package mydomain.myapp;
import android.content.Intent;
import android.os.Bundle;
public class MyApp extends comikit.droidscript.DroidScriptActivity
{
@Override
public void onCreate(Bundle bubble)
{
Intent intent = getIntent();
intent.putExtra("ScriptAsset", "MyApp.js");
super.onCreate(bubble);
}
}As a starting point, check out DroidZine (project by Jonas Beckman and Mikael Kindborg): http://github.com/kamidev/droidzine/JavaScript in WebKit
This example shows how to access Java from JavaScript in WebKit, via DroidScript.function onCreate(bundle)
{
var WebView = Packages.android.webkit.WebView;
var webview = new WebView(Activity);
webview.getSettings().setJavaScriptEnabled(true);
webview.addJavascriptInterface(Activity, "activity");
Activity.setContentView(webview);
var content =
"""<html>
<body>
<script>
function showToast(message) {
activity.eval(
"var Toast = Packages.android.widget.Toast;" +
"Toast.makeText(Activity, '" + message + "', " +
"Toast.LENGTH_SHORT).show();"); }
</script>
<h1>Take the pill</h1>
<input
type="button"
value="Take pill"
onclick="showToast('You have taken the red pill!')">
</body>
</html>""";
webview.loadData(content, "text/html", "utf-8");
}
MobileLua - An easy way to create Android apps (developed with MoSync)
Minimal painting app in Lua:function OnInit() SetColor(255, 255, 255) FillRect(0, 0, ScreenWidth(), ScreenHeight()) UpdateScreen() end function OnTouchDown(x, y) SetColor(0, 0, 0) FillRect(x - 5, y - 5, 10, 10) UpdateScreen() end function OnTouchDrag(x, y) OnTouchDown(x, y) endhttp://code.google.com/p/mobilelua/
0 comments:
Post a Comment