这里采用的是较早版本的phonegap框架中exec模块的实现,代码比较直观易懂,后来的版本为了提升代码的健壮性,增加了很多选择性代码。为了测试方便,直接使用简单版本的exec模块。更高版本的phonegap库也是在这个基础之上扩展的,可对比学习。
define("myphonegap/exec", function(require, exports, module) {
/**
* 执行cordova命令
* 同步:返回一个JSON字符串;异步:返回空字符串"",这个时候可以根据处理结果调用回调函数
* 参数:(1)success:命令执行成功回调函数
* (2)fail:命令执行失败回调函数
* (3)service:使用的服务名称
* (4)action:在cordova中运行的命令
* (5)args:0或多个参数组成的数组
*/
var myphonegap= require('myphonegap');
module.exports = function(success, fail, service, action, args) {
try {
var callbackId = service + cordova.callbackId++;//内部回调id
if (success || fail) {//至少传入了其中一个
myphonegap.callbacks[callbackId] = {success:success, fail:fail};
}
//将参数转化为JSON字符串去执行
var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true]));
if (r.length > 0) {
var v = JSON.parse(r);//将返回结果解析成对象
if (v.status === myphonegap.callbackStatus.OK) {
if (success) {//调用成功回调函数
try {
success(v.message);
} catch (e) {
console.log("Error in success callback: " + callbackId + " = " + e);
}
// 清除回调函数
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return v.message;
}
else if (v.status === myphonegap.callbackStatus.NO_RESULT) {
// 清除回调函数
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
else {// 错误
console.log("Error: Status="+v.status+" Message="+v.message);
// 调用失败回调函数
if (fail) {
try {
fail(v.message);
}
catch (e1) {
console.log("Error in error callback: "+callbackId+" = "+e1);
}
//清除回调函数
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return null;
}
}
} catch (e2) {
console.log("Error: "+e2);
}
};
});
这里最重要的一个函数是window提供的prompt这个函数,它实际上就是用来与android的交互的。当js调用prompt之后,本地代码WebChromeClient实例会回调一个onJsPrompt函数,在这个回调函数中接受js发送过来的参数信息,调用相应的service和action,同步的话直接返回结果,用result.confirm提交。异步调用,在调用结果返回之后,以回调ID和返回数据为参数,调用myphonegap.callbackSuccess或myphonegap.callbackError。
下面测试测试本地代码和JS代码的交互,由本地代码调用exec模块,给本地代码发送消息,本地代码解析消息调用对应的方法,返回结果后。
Js端添加测试代码,先请求exec模块。第一个请求执行的本地方法,类名为Math,方法名为plus,参数为15,成功后打印输出结果。第二个请求执行的本地方法,类名为Math,方法名为multiply,参数为15。
//测试exec模块
var exec = require("myphonegap/exec");
exec(function(ret){console.info("plus exec success!");console.info("15+15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'plus',15);
exec(function(ret){console.info("multiply exec success!");console.info("15*15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'multiply',15);
Js执行prompt方法后,android在WebChromeClient的onJsPrompt被调用,为了测试简单这里只用了action区分调用的本地方法没有使用service区分调用哪个类,plus操作为同步方法,直接返回,multiply模拟为异步方法,通过回调js里面的方法返回结果。
private class MyWebChromeClient extends WebChromeClient{
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
try {
//array的解析与js端的封装有关,字串过滤了”gap:”4个字符
JSONArray array = new JSONArray(defaultValue.substring(4));
String service = array.getString(0);
String action = array.getString(1);
String callbackId = array.getString(2);
if(action.equals("plus")){
int a = Integer.parseInt(message);
int ret = plus(a,a);
String str = "{\"status\":1,\"message\":" ;
str += ret;
str += "}";
result.confirm(str);
}else if(action.equals("multiply")){
int a = Integer.parseInt(message);
int ret = multiply(a,a);
String str = "{\"status\":1,\"message\":" ;
str += ret;
str += "}";
result.confirm("");
//此处采用异步模式,不使用result.confirm直接返回结果了。
mWebView.loadUrl("javascript:myphonegap.callbackSuccess(\""+callbackId+"\","+str+")");
}
} catch (JSONException e) {
e.printStackTrace();
}
return true;
}
本次测试采用了android真机测试。测试结果: