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();      }    }