2011年5月1日 星期日

[POC] Using javascript to develop Android Activity

好久沒玩些好玩的了....先來點小菜....(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