Android 使用Javascript作为脚本计算器

原创
2016/01/28 15:53
阅读数 574

原则上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");
        }
    }
    
    private String stringByEvaluatingJavaScriptFromString(String script)
{
    try
    {
        Field field_webviewcore = android.webkit.WebView.class.getDeclaredField("mWebViewCore");
        field_webviewcore.setAccessible(true);
        Object obj_webviewcore = field_webviewcore.get(jsEngine);
        Field field_BrowserFrame = obj_webviewcore.getClass().getDeclaredField("mBrowserFrame");
        field_BrowserFrame.setAccessible(true);
        Object obj_frame = field_BrowserFrame.get(obj_webviewcore);
        Method method_stringByEvaluatingJavaScriptFromString = obj_frame.getClass().getMethod("stringByEvaluatingJavaScriptFromString", new Class[] {
                java.lang.String.class
        });
        Object obj_value = method_stringByEvaluatingJavaScriptFromString.invoke(obj_frame, new Object[] {
                script
        });
        return String.valueOf(obj_value);
    }
    catch(Exception e)
    {
        e.printStackTrace();
        jsEngine.loadUrl(script);
    }

    return null;
}
    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{
                stringByEvaluatingJavaScriptFromString("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();
    }

}


展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部