Android 使用Javascript作为脚本计算器
KalArispe
9年前
来自: http://my.oschina.net/ososchina/blog/610964
原则上Java 本身不支持脚本计算,早期通过ScriptManager或者J2V8支持脚本计算,但在Android中阉割了此部分功能,因此,为了能进行脚本计算,我们需要特别封装
import android.content.Context; import android.database.DataSetObserver; import android.graphics.Bitmap; import android.os.Build; import android.os.Handler; import android.os.Message; import android.util.Log; import android.webkit.ConsoleMessage; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import org.json.JSONException; import org.json.JSONObject; /** * Created by Noah on 2016/1/23. */ public class JavaScriptManager implements Handler.Callback{ //计算引擎 private WebView jsEngine; //计算结果回调 private Handler.Callback mCallback; //判断页面是否加载完成 private boolean isLoadFinished; //表示加载完成 public final static int STATE_FINISHED = 200; //返回正确结果 public final static int STATE_RESULT_OK = 100; //计算出错 public final static int STATE_RESULT_FAILED = -100; //加载开始 public final static int STATE_STARTING = 300; private JavaScriptManager(Context context,Handler.Callback callback) { jsEngine = new WebView(context); initJsEngine(); this.mCallback = callback; } public static JavaScriptManager newInstance(Context context,Handler.Callback callback) { return new JavaScriptManager(context,callback); } private void initJsEngine() { if(jsEngine!=null) { WebSettings settings = jsEngine.getSettings(); settings.setCacheMode(WebSettings.LOAD_NO_CACHE); settings.setAppCacheEnabled(false); settings.setJavaScriptCanOpenWindowsAutomatically(false); settings.setSupportMultipleWindows(false); settings.setSaveFormData(false); settings.setGeolocationEnabled(false); settings.setJavaScriptEnabled(true); settings.setSupportZoom(false); settings.setDefaultTextEncodingName("utf-8"); jsEngine.setWebChromeClient(new JsEngineChromeClient(this)); jsEngine.setWebViewClient(new JsEngineWebClient(this)); } } @Override public boolean handleMessage(Message msg) { if(msg!=null) { if(msg.what==STATE_RESULT_OK) { return mCallback.handleMessage(msg); } else if(msg.what==STATE_FINISHED) { isLoadFinished = true; }else if(msg.what==STATE_STARTING){ isLoadFinished = false; }else{ Log.e("JavaScriptManager","ErrorCode="+msg.what+"description="+(String)msg.obj); //防止脚本出错之后影响程序运行,有必要重新刷新 if(msg.what==STATE_RESULT_FAILED) { jsEngine.reload(); } return mCallback.handleMessage(msg); } } return false; } public void loadWebData(String html) { if(isLoadFinished && jsEngine!=null) { jsEngine.loadData(html,"text/html;charset=utf-8","utf-8"); } } public void executeScript(String script) { if(jsEngine!=null) { if(Build.VERSION.SDK_INT>=19) { jsEngine.evaluateJavascript(script, new ValueCallback<String>() { @Override public void onReceiveValue(String message) { Message msg = new Message(); try { JSONObject json = new JSONObject(message); msg.what = json.optInt("code",STATE_RESULT_OK); msg.obj = json.optString("result",""); } catch (JSONException e) { e.printStackTrace(); msg.what = -STATE_RESULT_FAILED; } JavaScriptManager.this.handleMessage(msg); } }); }else{ jsEngine.loadUrl("javascript:"+script); } } } private static class JsEngineWebClient extends WebViewClient{ public Handler.Callback callback; public JsEngineWebClient(Handler.Callback callback){ this.callback = callback; } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); Message msg = new Message(); msg.what = errorCode; msg.obj = description; callback.handleMessage(msg); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Message msg = new Message(); msg.what = STATE_FINISHED; msg.obj = "finished"; callback.handleMessage(msg); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); Message msg = new Message(); msg.what = STATE_STARTING; msg.obj = "finished"; callback.handleMessage(msg); } } private static class JsEngineChromeClient extends WebChromeClient{ public Handler.Callback callback; public JsEngineChromeClient(Handler.Callback callback){ this.callback = callback; } @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { if (consoleMessage.messageLevel()==ConsoleMessage.MessageLevel.ERROR) { Log.e("JavaScriptManager" + consoleMessage.lineNumber(), consoleMessage.message()); }else if(consoleMessage.messageLevel()==ConsoleMessage.MessageLevel.WARNING){ Log.w("JavaScriptManager"+consoleMessage.lineNumber(),consoleMessage.message()); }else{ Log.i("JavaScriptManager"+consoleMessage.lineNumber(),consoleMessage.message()); } return super.onConsoleMessage(consoleMessage); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { result.confirm(); Message msg = new Message(); try { JSONObject json = new JSONObject(message); msg.what = json.optInt("code",STATE_RESULT_OK); msg.obj = json.optString("result",""); } catch (JSONException e) { e.printStackTrace(); msg.what = STATE_RESULT_FAILED; } callback.handleMessage(msg); return true; } } }
当然,我们还需要初始化,以保证我们的脚本可以更加健壮
public class ScriptFactory { public String createScript() { StringBuilder sb = new StringBuilder(); sb.append("<!DOCTYPE HTML>"); sb.append("\r\n"); sb.append("<html>"); sb.append("\r\n"); sb.append("<head>"); sb.append("\r\n"); sb.append("<meta charset='utf-8'>"); sb.append("\r\n"); sb.append("<meta http-equiv='cache-control' content='no-cache,private'>"); sb.append("\r\n"); sb.append("<title>Javascript Engine</title>"); sb.append("\r\n"); sb.append("</head>"); sb.append("\r\n"); sb.append("<body>"); sb.append("\r\n"); sb.append("<script type='text/javascript'>"); sb.append("\r\n"); sb.append("window.handleEval = function( s)"); sb.append("\r\n"); sb.append("{"); sb.append("\r\n"); sb.append(" try{"); sb.append("\r\n"); sb.append(" var result = window.eval(s); "); sb.append("\r\n"); sb.append(" var ret = {code:100,result:result}; "); sb.append("\r\n"); sb.append(" alert(JSON.stringify(ret)); "); sb.append("\r\n"); sb.append(" }"); sb.append("\r\n"); sb.append(" catch(e)"); sb.append("\r\n"); sb.append(" {"); sb.append("\r\n"); sb.append(" var ret = {code:-100,result:'Express Error['+s+']==>'+e.message}; "); sb.append("\r\n"); sb.append(" alert(JSON.stringify(ret)); "); sb.append("\r\n"); sb.append(" }"); sb.append("\r\n"); sb.append("}"); sb.append("\r\n"); sb.append("</script>"); sb.append("\r\n"); sb.append("</body>"); sb.append("\r\n"); sb.append("</html>"); return sb.toString(); } }