上次做的僅止於setText這個簡單的動作, 那像是處理click這類的怎做?

先拿click來做範例, 在Android中, 如果要替一個view加上處理click動作要利用到View.setOnClickListener, 但setOnClickListener的參數是onClickListener, 這是一個java interface, 在Rhino中如何實作一個Java interface?

首先, 不要被Rhino的文件給騙了, 那有問題(還花了我一個晚上看 :( ), Rhino的source codes裡有個example目錄, 裡面有隻SwingApplication.js, 答案就在這邊

因此, onClick就會像是這樣:

var button1 = findview(R.id(‘button1’));

button1.setOnClickListener(function(view, methodName)
{
     if (methodName == “onClick”) {
         log(“MyScript”, “clicked”);
} });

在這function內, 必須要自己判斷methodName來知道是哪個method被呼叫到, 應該是所有的java interface都可以用這樣去實作

OK, 這樣…很醜…繼續改進

好久沒玩些好玩的了….先來點小菜….(POC = Prove of concept, 只是剛好有些想法, 所以來證明一下可不可行)

基本的想法是想用類似下面的javascript來寫出Android Activity(當然, 要延伸成其他的應該也沒問題)

https://gist.github.com/950551.js?file=gistfile1

這一個script有幾個簡單的組成 : 

  1. getcontentview : 用來指定這個activity所要用的main layout ID
  2. oncreate : 在Activity oncreate時被呼叫
  3. onresume : 在Activity onresume時被呼叫
  4. onpause : 在Activity onpause時被呼叫
  5. 基本延伸函數 : 
    • log: 等同於Log.d
    • findview: 等同於findViewById
    • R.layout(“layoutname”): 等同於R.layout.layoutname (同樣的R.id(“”)也是…)

因為要在原本Android Java的平台上跑Javascript, 所以需要一個Javascript engine做為一個平台, 這邊選用的是Mozilla Rhino

Rhino可以幾乎無痛的在Android上使用, 當然SL4A有更多的script engine的選擇, 但SL4A的方向不同, 我是想弄一個也可以寫一般的Android program的script

初始化Javascript engine

第一個版本, 只是拿來實驗的, 所以script先放在asset裡面, 這一段會初始化基本的script engine, 然後從asset目錄裡面載入"init.js"

首先要先初始化一個Context instance, 所有的script執行都是要透過這個Context的, 不用後可以透過.exit()來釋放資源

jsContext.setOptimizationLevel(-1);

setOptimizationLevel(-1)這段很重要, 沒設成-1的話是無法在Android上跑的, 原因是Rhino會把javascript編譯成java bytecode以增加執行效率, 但這bytecode是標準的java bytecode而非Android dalvik的bytecode, 設成-1的話, Rhino會改用Intepreter來跑, 而非先compile

Host objects 和 Host functions

ScriptableObject.putProperty(jsScope, “R”, RObj);

ScriptableObject.putProperty(jsScope, “log”, new Log());

ScriptableObject.putProperty(jsScope, “findview”, new FindViewById(this));

這一段包裝了一個host object和兩個host functions讓script使用(亦即是前述的5)

我把R包裝成另一個Object供javascript使用(因為我還沒找到如何讓Rhino使用static fields), 這包裝叫Rwrapper.java:

只是把R用reflection包裝一下而已

至於javascript function的部份, 其實也不難, 只要implement Function.call就可以了, 這邊實作了兩個(繼承自BaseFunction), Log.java和FindViewById.java

Log.java:

FindViewById.java:

不管是Object或是Function, 其實都是被當做objects來看待, 所以只要利用ScriptableObject.putProperty放到目前的scope去就可以給script取用了

目前看來, 這想法可行, 而且還蠻容易的, 缺乏的只是一些包裝而已, 甚至直接對view做操作也是可行的, 有空(會有空嗎?) 再來玩深入點.. :P