Wednesday, February 13, 2013

Beanshell Scripting in Android

I needed to add a new feature to one of my apps. The ability to send the app a scripted set of instructions to execute when needed.
So to start I decided to try and get the Scripting part to work first, I will be adding push notifications at a later stage using GCM, but for now I just wanted to see if it would be possible to actually run a script in Android.

I had no idea where to start so I asked a Android programming buddy of mine, Toby Kurien (I hope he doesn't mind me mentioning him), and he suggested I look at Beanshell. Basically its Java in scripting form. And it looked perfect for what I needed. The only issue is I could find virtually zero documentation on how to include this in an Android project, it works in Java projects, and so I just presumed, based on Android using Java and Toby's suggestion, that it would work.

I set out to get it to work.

First things first, we need the library.
For a whole bunch of getting started documentation, you can check out the website.

I just downloaded the entire package, but I should imagine using just the core and classgen packages would be sufficient.

Anyway, onto the code.

Firstly you need to import the Interpreter like any other class and instantiate the Interpreter Object.

NOTE: I am using this in a Service, however it will work in an Activity or any other component I think.

//Imports the Interpreter
import bsh.Interpreter;

public class SomeService extends Service{

//Instantiates the interpreter object
Interpreter interpreter = new Interpreter();
private final IBinder mBinder = new SomeBinder();

 @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

 @Override onCreate(){

    }
}

So now we have the Interpreter object and have imported it.
Now we can start using it.

I am just putting this in the onCreate() method, but you can run this in a method or a Thread or however you wish. I have also used it in two different ways, one is to direct it to a file which contains the script and the other is using a reader which has read the script. I also send an instance of the Service to the Interpreter which allows me run methods and send my application data or instructions from the script, which I think is vital for me


import bsh.Interpreter;

public class SomeService extends Service{

Interpreter interpreter = new Interpreter();
private final IBinder mBinder = new SomeBinder();
SomeService serv;

 @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

 @Override onCreate(){
    serv = this;
    // Creates a File object with the location of the script basic.bsh 
    // (bsh - beanshell script)
    File file = new File(Environment.getExternalStorageDirectory() + "/script",
    "basic.bsh");
            try {
                // Creates a Reader which has read the file
                Reader reader = new FileReader(file);
                // Sets the variable myapp in the script to Service instance
                i.set("myapp", serv);
                //eval() is used to either run a String as a script or the reader
                i.eval(reader);
                //source() is used to run a script from the path name
                i.source(file.getAbsolutePath());
            //EvalError is from the Beanshell Interpreter, and the other two are the standard IO operation exceptions
            } catch (EvalError evalError) {
                evalError.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
   public void hello(String myName){
   Log.i("TAG","My name is: "+myName);
   }
}

I included a hello() method, which I will use in this example to demonstrate how to call it form within the script.

I must also mention, you can call Android classes into the script, I haven't used it extensively...actually I just used android logging, however, it works flawlessly. I will include an example in the script just for demonstration purposes.

My script will look something like this:

import android.util.Log;
myapp.hello("Sean");
Log.e("TAG","Hello from Beanshell");

And that's that. I use TextWrangler to create the script and then just load it onto the sdcard in the folder 'script'. You can of course decide on however you wish to put the script there, I am certain you could even include the scripts in the Android project itself, but this is what I did.

It's very simple and very effective. If anyone needs any help, please comment and I will get back to you as soon as I can. My next post will probably be on how to use GCM.

4 comments:

  1. Hey Sean,

    Sorry for this incompetent question. How are you compiling with ant? I tried putting the bsh-2.0b4.jar file in my jre lib/ext directory after the build was failing. After moving the .jar to that directory, the ant build says it's successful, but I get a java.lang.NoClassDefFoundError exception during runtime because of the Interpreter. What am I doing wrong?

    ReplyDelete
    Replies
    1. Hey there. Sorry for the huge delay, i was not notified of the comment. Are you using an IDE? If so which one? I was using Intellij IDEA which was handling all the compiling for me.

      Delete
    2. Sorry for the even longer reply. I never got it working, but I'm still curious about it.

      I was using ADT Eclipse. I'll check out the Intellij IDEA. Thanks.

      Delete
  2. I am interested in this. could you please add the source code of your tutorial.

    Thank you,
    Hisham

    ReplyDelete