Android(安卓)混合了 JSON 的 Android(安卓)应用程序
16lz
2022-05-20
通过WebView在javascript里调用java 方法代码, 在java代码里执行javascript代码, 二者通过json进行数据交换
设置 WebView 小部件
将一个名为 browser 的 Activity 范围变量捆绑到了 WebView 控件。WebView 是一个相当复杂的类,可高度定制。例如,您需要设置几个类,以得到与 web 浏览器相关的预期函数。这是程序员必须投入一定精力来得到一些有用函数的地方之一。但是,此定制是没有限制的。对于此应用程序的目的来说,WebView 控件已经进行了最低限度的部署。
WebViewClient 提供用于捕获各种事件的钩子,这些事件包括页面加载开始和结束、表单重新提交、键盘截取以及程序员喜欢跟踪并操纵的很多其他事件。类似地,您需要 WebChromeClient 的一个实例,用于允许诸如非常有用的 alert() JavaScript 函数之类的函数。使用 WebSettings 来为控件启用 JavaScript。
连接 JavaScript 接口
下一步是启用 Activity 中的 Java 代码,以与 WebView 管理的 HTML 文件中的 JavaScript 代码交互。这是通过调用 addJavascriptInterface() 方法完成的,如 清单 4 所示。
该函数的参数是一个 Java 类的实例和一个名称空间标识符。例如,对于这个应用程序,您定义一个 calc 名称空间,并实现名为 CalculatorHandler 的类中的代码,如 清单 5 所示。
清单 5. CalculatorHandler 实现
在 JavaScript 环境中,通过 window.calc.methodname 语法访问 CalculatorHandler 的方法。例如,CalculatorHandler 实现一个名为 Info() 的方法,后者接受一个字符串参数并将之写到应用程序日志中。要从 JavaScript 环境访问此方法,可使用类似这样的语法:window.calc.Info("write this string to the application log!");。
基本了解了如何从 JavaScript 代码调用 Java 代码之后,我们再来看 清单 6 中的 index.html 文件,看各种方法是如何被调用的。
清单 6. WebView 控件中呈现(和执行)的 index.html
仔细研究一下 清单 6 末尾的按钮处理程序。基本上,这些按钮处理程序都调用 window.calc 名称空间中的方法,这些方法在 AndroidJSON.java 中的 CalculatorHandler 类中实现。
清单 5 和 清单 6 协同工作,演示了 JavaScript 环境中初始化的和 Java 源文件中实现的代码交互。但是如何从 Activity 代码中初始化一些您想要在 WebView 中发生的动作呢?
现在应该更深入地来看 Java 代码了。
插入 JavaScript 代码
从将一个数学公式传递到 JavaScript 代码进行计算这样一个任务开始。JavaScript 最伟大(也最危险)的特性之一是 eval() 函数。eval() 函数允许字符串代码的运行时计算。在本例中,您从 EditText 控件接受一个字符串并传递到 JavaScript 环境进行计算。具体来说,我们调用 清单 6 中的 PerformSimpleCalculation() 函数。
清单 7 包含 AndroidJSON.java 中的代码,它负责处理按钮选择。
清单 7. 从 Java 调用 PerformSimpleCalculation() JavaScript 函数
不管此方法有多少行,这里唯一要关注的是 browser.loadurl() 行,它传递一个格式字符串:javascript:<code to execute>。
此 JavaScript 代码被注入到 WebView 的当前页面并执行。这样,Java 代码就可以执行 WebView 中定义的 JavaScript 代码了。
在 Simple 例子中,传递了一个字符串。但是,当需要处理更复杂的结构时该怎么办呢?这就是 JSON 可派上用场的地方。清单 8 展示了 PerformComplexCalculation() 函数的调用,该函数参见 清单 6。
清单 8. 通过传递一个 JSON 对象调用更复杂的函数
研究一下 清单 6 中的 JavaScript 函数 PerformComplexCalculation。注意,传递进来的参数不是字符串,而是您自己创建的一个对象。
* operation - 要处理的函数或过程的名称
* arguments - 这是一个整数数组
对象只包含两个属性,但是完全可以更复杂,以满足更高的需求。在本例中,PerformComplexCalculation() JavaScript 函数支持两种不同的操作:addarray 和 multarray。当这些操作在调用时完成其工作时,通过调用函数 window.calc.setAnswer,将结果传递回 Java 代码。这里,您看到了 Java 和 JavaScript 代码之间的双向数据流。
在本例中,您传递了一个 JSON 对象,但是得到的一条经验是,在处理从 Java 代码返回来的 Java 字符串时,它有助于将它们转换成 JavaScript 字符串。这可以像本例中一样通过将值传递给 String 函数来做到:eval(String(formula));。
JavaScript eval() 函数使用 JavaScript 字符串。无需转换的话,eval 函数基本上不做任何事情。
对于一个稍微复杂一点的例子,鼓励您好好看一下 Dynamic 按钮在 WebView 中被选中时的代码段。
要完成代码例子,来看一下将一个字符串数组从 JavaScript 环境传递到 Java 环境。
交换 JSON 对象
示例应用程序 (index.html) 中的 JavaScript 代码将本地函数调用记录到一个名为 cmdHistory 的页面级别数组中。每次调用函数时,您都将一个新条目添加到该数组中。例如,当 dynamicfunction() 被调用时,一个新的字符串被存储:cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";。
关于此方法,没有什么特别的地方;它只是一个在页面级别收集使用数据的例子。也许该数据存储在 Android 应用程序的数据库中会有用。此数据如何回到 Java 代码呢?
要发送字符串对象数组,您调用 JSON.stringify 函数,将数组作为参数传递进来。根据需要,stringify 函数可以允许定制一个复杂对象的特定属性如何被格式化。关于这是如何完成的更多信息,可以参考 json.org 中的解释
设置 WebView 小部件
// connect to our browser so we can manipulate itbrowser = (WebView) findViewById(R.id.calculator);// set a webview client to override the default functionalitybrowser.setWebViewClient(new wvClient());// get settings so we can config our WebView instanceWebSettings settings = browser.getSettings();// JavaScript? Of course!settings.setJavaScriptEnabled(true);// clear cachebrowser.clearCache(true);// this is necessary for "alert()" to workbrowser.setWebChromeClient(new WebChromeClient());// add our custom functionality to the javascript environmentbrowser.addJavascriptInterface(new CalculatorHandler(), "calc");// uncomment this if you want to use the webview as an invisible calculator!//browser.setVisibility(View.INVISIBLE);// load a page to get things startedbrowser.loadUrl("file:///android_asset/index.html");// allows the control to receive focus// on some versions of Android the webview doesn't handle input focus properly// this seems to make things work with Android 2.1, but not 2.2// browser.requestFocusFromTouch();
将一个名为 browser 的 Activity 范围变量捆绑到了 WebView 控件。WebView 是一个相当复杂的类,可高度定制。例如,您需要设置几个类,以得到与 web 浏览器相关的预期函数。这是程序员必须投入一定精力来得到一些有用函数的地方之一。但是,此定制是没有限制的。对于此应用程序的目的来说,WebView 控件已经进行了最低限度的部署。
WebViewClient 提供用于捕获各种事件的钩子,这些事件包括页面加载开始和结束、表单重新提交、键盘截取以及程序员喜欢跟踪并操纵的很多其他事件。类似地,您需要 WebChromeClient 的一个实例,用于允许诸如非常有用的 alert() JavaScript 函数之类的函数。使用 WebSettings 来为控件启用 JavaScript。
连接 JavaScript 接口
下一步是启用 Activity 中的 Java 代码,以与 WebView 管理的 HTML 文件中的 JavaScript 代码交互。这是通过调用 addJavascriptInterface() 方法完成的,如 清单 4 所示。
该函数的参数是一个 Java 类的实例和一个名称空间标识符。例如,对于这个应用程序,您定义一个 calc 名称空间,并实现名为 CalculatorHandler 的类中的代码,如 清单 5 所示。
清单 5. CalculatorHandler 实现
// Javascript handlerfinal class CalculatorHandler{ private int iterations = 0; // write to LogCat (Info) public void Info(String str) { iterations++; Log.i("Calc",str); } // write to LogCat (Error) public void Error(String str) { iterations++; Log.e("Calc",str); } // sample to retrieve a custom - written function with the details provided // by the Android native application code public String GetSomeFunction() { iterations++; return "var q = 6;function dynamicFunc(v) { return v + q; }"; } // Kill the app public void EndApp() { iterations++; finish(); } public void setAnswer(String a) { iterations++; Log.i(tag,"Answer [" + a + "]"); } public int getIterations() { return iterations; } public void SendHistory(String s) { Log.i("Calc","SendHistory" + s); try { JSONArray ja = new JSONArray(s); for (int i=0;i<ja.length();i++) { Log.i("Calc","History entry #" + (i+1) + " is [" + ja.getString(i)+ "]"); } } catch (Exception ee) { Log.e("Calc",ee.getMessage()); } }}
在 JavaScript 环境中,通过 window.calc.methodname 语法访问 CalculatorHandler 的方法。例如,CalculatorHandler 实现一个名为 Info() 的方法,后者接受一个字符串参数并将之写到应用程序日志中。要从 JavaScript 环境访问此方法,可使用类似这样的语法:window.calc.Info("write this string to the application log!");。
基本了解了如何从 JavaScript 代码调用 Java 代码之后,我们再来看 清单 6 中的 index.html 文件,看各种方法是如何被调用的。
清单 6. WebView 控件中呈现(和执行)的 index.html
<html><head><meta name="viewport" content="width=device-width,initial-scale=0.25, user-scalable=yes" /><title>Android to JavaScript with JSON</title></head><script language="JavaScript">var cmdHistory = new Array();function startup() { try { window.calc.Info("Starting up...."); cmdHistory[cmdHistory.length] = "startup"; } catch (ee) { }}function PerformSimpleCalculation(formula) { try { cmdHistory[cmdHistory.length] = "PerformSimpleCalculation"; var answer = eval(String(formula)); document.getElementById('data').value = answer; window.calc.setAnswer(answer); } catch (ee) { window.calc.Error(ee); }}function PerformComplexCalculation(andmethod) { try { /* * argument to this function is a single object with 2 "members or properties" * operation: this is a string naming what we want the function to do. * array of arguments: this is an array of integers * */ //alert(andmethod.operation); //alert(andmethod.arguments.length); if (andmethod.operation == "addarray") { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-addarray"; var i; var result = 0; for (i=0;i<andmethod.arguments.length;i++) { result += andmethod.arguments[i]; } document.getElementById('data').value = result; window.calc.setAnswer(result); } if (andmethod.operation == "multarray") { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-multarray"; var i; var result = 1; for (i=0;i<andmethod.arguments.length;i++) { result *= andmethod.arguments[i]; } document.getElementById('data').value = result; window.calc.setAnswer(result); } } catch (ee) { window.calc.Error(ee); }}function dynamicfunction(){ try { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic"; eval(String(window.calc.GetSomeFunction())); var result = dynamicFunc(parseInt(document.getElementById('data').value)); document.getElementById('data').value = result; }catch (ee) { alert(ee); }}</script><body ><center><h3>Running in Web View :)</h3>this is some sample text here <br /><input type="text" id="data" value="starting value"><br /><button onclick="window.calc.Info(document.getElementById('data').value);">Log Info</button> <button onclick="window.calc.Error(document.getElementById('data').value);">Log Error</button><br /><button onclick="dynamicfunction();">Dynamic</button><button onclick="alert(String(window.calc.getIterations()));">How Many Calls</button><button onclick="window.calc.SendHistory(JSON.stringify(cmdHistory));"> History</button><button onclick="if (window.confirm('End App?')) window.calc.EndApp();">Kill This App</button><br /></center></body></html>
仔细研究一下 清单 6 末尾的按钮处理程序。基本上,这些按钮处理程序都调用 window.calc 名称空间中的方法,这些方法在 AndroidJSON.java 中的 CalculatorHandler 类中实现。
清单 5 和 清单 6 协同工作,演示了 JavaScript 环境中初始化的和 Java 源文件中实现的代码交互。但是如何从 Activity 代码中初始化一些您想要在 WebView 中发生的动作呢?
现在应该更深入地来看 Java 代码了。
插入 JavaScript 代码
从将一个数学公式传递到 JavaScript 代码进行计算这样一个任务开始。JavaScript 最伟大(也最危险)的特性之一是 eval() 函数。eval() 函数允许字符串代码的运行时计算。在本例中,您从 EditText 控件接受一个字符串并传递到 JavaScript 环境进行计算。具体来说,我们调用 清单 6 中的 PerformSimpleCalculation() 函数。
清单 7 包含 AndroidJSON.java 中的代码,它负责处理按钮选择。
清单 7. 从 Java 调用 PerformSimpleCalculation() JavaScript 函数
btnSimple.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.i(tag,"onClick Simple"); // Perform action on click try { String formulaText = formula.getText().toString(); Log.i(tag,"Formula is [" + formulaText + "]" ); browser.loadUrl("javascript:PerformSimpleCalculation(" + formulaText + ");"); } catch (Exception e) { Log.e(tag,"Error ..." + e.getMessage()); } } });
不管此方法有多少行,这里唯一要关注的是 browser.loadurl() 行,它传递一个格式字符串:javascript:<code to execute>。
此 JavaScript 代码被注入到 WebView 的当前页面并执行。这样,Java 代码就可以执行 WebView 中定义的 JavaScript 代码了。
在 Simple 例子中,传递了一个字符串。但是,当需要处理更复杂的结构时该怎么办呢?这就是 JSON 可派上用场的地方。清单 8 展示了 PerformComplexCalculation() 函数的调用,该函数参见 清单 6。
清单 8. 通过传递一个 JSON 对象调用更复杂的函数
btnComplex.setOnClickListener(new OnClickListener(){ public void onClick(View v) { Log.i(tag,"onClick Complex"); // Perform action on click try { String jsonText = ""; if (flipflop == 0) { jsonText = "{ \"operation\" : \"addarray\",\"arguments\" : [1,2,3,4,5,6,7,8,9,10]}"; flipflop = 1; } else { jsonText = "{ \"operation\" : \"multarray\",\"arguments\" : [1,2,3,4,5,6,7,8,9,10]}"; flipflop = 0; } Log.i(tag,"jsonText is [" + jsonText + "]" ); browser.loadUrl("javascript:PerformComplexCalculation(" + jsonText + ");"); } catch (Exception e) { Log.e(tag,"Error ..." + e.getMessage()); } }});
研究一下 清单 6 中的 JavaScript 函数 PerformComplexCalculation。注意,传递进来的参数不是字符串,而是您自己创建的一个对象。
* operation - 要处理的函数或过程的名称
* arguments - 这是一个整数数组
对象只包含两个属性,但是完全可以更复杂,以满足更高的需求。在本例中,PerformComplexCalculation() JavaScript 函数支持两种不同的操作:addarray 和 multarray。当这些操作在调用时完成其工作时,通过调用函数 window.calc.setAnswer,将结果传递回 Java 代码。这里,您看到了 Java 和 JavaScript 代码之间的双向数据流。
在本例中,您传递了一个 JSON 对象,但是得到的一条经验是,在处理从 Java 代码返回来的 Java 字符串时,它有助于将它们转换成 JavaScript 字符串。这可以像本例中一样通过将值传递给 String 函数来做到:eval(String(formula));。
JavaScript eval() 函数使用 JavaScript 字符串。无需转换的话,eval 函数基本上不做任何事情。
对于一个稍微复杂一点的例子,鼓励您好好看一下 Dynamic 按钮在 WebView 中被选中时的代码段。
要完成代码例子,来看一下将一个字符串数组从 JavaScript 环境传递到 Java 环境。
交换 JSON 对象
示例应用程序 (index.html) 中的 JavaScript 代码将本地函数调用记录到一个名为 cmdHistory 的页面级别数组中。每次调用函数时,您都将一个新条目添加到该数组中。例如,当 dynamicfunction() 被调用时,一个新的字符串被存储:cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";。
关于此方法,没有什么特别的地方;它只是一个在页面级别收集使用数据的例子。也许该数据存储在 Android 应用程序的数据库中会有用。此数据如何回到 Java 代码呢?
要发送字符串对象数组,您调用 JSON.stringify 函数,将数组作为参数传递进来。根据需要,stringify 函数可以允许定制一个复杂对象的特定属性如何被格式化。关于这是如何完成的更多信息,可以参考 json.org 中的解释
更多相关文章
- Android单元测试 - 如何开始?
- android中listview的setAdapter()和getAdapter()
- Android简单调用相机Camera功能,实现打开照相功能
- Android(安卓)Toolchain与Bionic Libc
- Android(安卓)Studio NDK及so文件开发 以及常见错误
- android进行录音功能并保存播放
- Android(安卓)Binder简介
- Android(安卓)之 ServiceManager与服务管理
- 箭头函数的基础使用