好久没写过东西了,似乎只是毕业前写了一点东西,博客园开通的时间正是我去现在公司实习的时间。这一年多学了不少的东西,待我慢慢写来。
有一阵时间我一直在研究如何在android上写js程序,当然是去掉webview这个控件 ,我想的是直接和android交互,看了网上一个被复制了很多分的文章,给我了一个提示,我找了其中一个最简单的工具 rhino javascript。刚开始用是在java程序上,调用例子和自己修改了一些地方测试都可以通过,但是放到了android上没有通过,OMG,白玩了么不是。不通过的原因是一个底层函数 ClassLoader ,java和android上机制有点差别,晕!
后来百度的时候偶然间看到一个博友跟我想法一样并且他已经放到了android测试通过了,找到了评论 里面写的是用rhino最开始的版本是可以放到android上的。
果断换掉最新版本的jar包,把最原始的版本jar包放到了android程序里,运行 通过了。
好吧问题才刚刚开始,android上的js不能定义太多全局变量和函数,会产生堆栈溢出的问题。并且提示运行错误暂时还是有问题的。只能提示主js的错误。load进来的js错误是不提示的。这些用log慢慢的也可以调试,忍了!最开始学js的时候也是一个个alert调试错误,嘎嘎!好傻。
关于堆栈溢出的问题,我想到了一个办法就是再运行时候load进来其它的js,这样就不会产生溢出了,产生了一个新的问题,直接报错了。大体是多线程不可以调用load函数,错误原因是没有Context给当前线程。因为技术原因这里必须用多线程。所以改写spawn。写了一个Thread函数。
源码如下
1 public static void Thread(Context _cx, final Scriptable thisObj, 2 Object[] args, Function funObj) 3 { 4 for (int i=0; i < args.length; i++) { 5 final Function func=(Function) args[i]; 6 new Thread(new Runnable(){ 7 8 @Override 9 public void run() {10 try {11 func.call(cx, global, thisObj, new Object[0]);12 } catch (JavaScriptException ex) {13 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);14 }15 }16 17 }).start();18 }19 }
/** * Get a context associated with the current thread, creating * one if need be. * * The Context stores the execution state of the JavaScript * engine, so it is required that the context be entered * before execution may begin. Once a thread has entered * a Context, then getCurrentContext() may be called to find * the context that is associated with the current thread. ** Calling
enter()
will * return either the Context currently associated with the * thread, or will create a new context and associate it * with the current thread. Each call toenter()
* must have a matching call toexit()
. For example, ** Context cx = Context.enter(); * ... * cx.evaluateString(...); * cx.exit(); ** @return a Context associated with the current thread * @see org.mozilla.javascript.Context#getCurrentContext * @see org.mozilla.javascript.Context#exit */ public static Context enter() { Thread t = Thread.currentThread(); Context cx = (Context) threadContexts.get(t); if (cx == null) { cx = new Context(); cx.currentThread = t; threadContexts.put(t, cx); } synchronized (cx) { cx.enterCount++; } return cx; } /** * Exit a block of code requiring a Context. * * Callingexit()
will disassociate the Context with the * current thread if the matching call toenter()
* had created a new Context. * Once the current thread no longer has an associated Context, * it cannot be used to execute JavaScript until it is again associated * with a Context. * * @see org.mozilla.javascript.Context#enter */ public synchronized void exit() { if (--enterCount == 0) { threadContexts.remove(currentThread); currentThread = null; } }
enterCount是记录当前cx线程个数的。这样很明显啊,少了一个函数啊,如何增加新的线程?没有找到对应的函数,所以添加了一个函数
public static Context enter(Context cx) { if(cx==null)return null; Thread t = Thread.currentThread(); threadContexts.put(t, cx); cx.enterCount++; return cx; }
重新改写Thread函数
1 public static void Thread(Context _cx, final Scriptable thisObj, 2 Object[] args, Function funObj) 3 { 4 for (int i=0; i < args.length; i++) { 5 final Function func=(Function) args[i]; 6 new Thread(new Runnable(){ 7 8 @Override 9 public void run() {10 try {11 Context.enter(cx);12 func.call(cx, global, thisObj, new Object[0]);13 } catch (JavaScriptException ex) {14 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);15 }16 }17 18 }).start();19 }20 }
好吧load函数在新线程里面没有问题了。
后续我将把android js 游戏源码发布上来共大家品评。基本上游戏的大部分功能都已经完成,待优化。
ps:cx 和 global是全局函数。具体代码请参见rhino的源码