好久沒玩些好玩的了….先來點小菜….(POC = Prove of concept, 只是剛好有些想法, 所以來證明一下可不可行)
基本的想法是想用類似下面的javascript來寫出Android Activity(當然, 要延伸成其他的應該也沒問題)
https://gist.github.com/950551.js?file=gistfile1
這一個script有幾個簡單的組成 :
- getcontentview : 用來指定這個activity所要用的main layout ID
- oncreate : 在Activity oncreate時被呼叫
- onresume : 在Activity onresume時被呼叫
- onpause : 在Activity onpause時被呼叫
- 基本延伸函數 :
- 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