因?yàn)轫?xiàng)目需要做一個(gè)活動乘盼,而這個(gè)活動的信息是源于HTML5寫的崇堰,而這個(gè)操作網(wǎng)頁的過程上沃于,
是需要到與原生APP這邊交互的,因?yàn)檫@就增加了一個(gè)需求海诲,那就是與JS交互繁莹。
目前很流行的庫有WebviewJavaScriptBridge和OVGap,這兩個(gè)庫都是讓webview與JS建立起一條橋梁特幔,
這樣就可以相互通信了咨演。
花了兩天的時(shí)間,反反復(fù)復(fù)地研究了WebviewJavaScriptBridge和OVGap這兩個(gè)庫蚯斯,也在網(wǎng)上搜索了很多的
相關(guān)博客看薄风,可是都沒有滿足我的需求。網(wǎng)上的教程幾乎都是webview給調(diào)用JS拍嵌,使用系統(tǒng)提供的方法遭赂,這是
想學(xué)Easy就可以做到的,但是如果想讓JS調(diào)用我們的原生的方法横辆,那就不容易了撇他,就需要一條橋梁,在JS響應(yīng)的
時(shí)候能回調(diào)OC的方法狈蚤。這兩個(gè)庫都是可以滿足我們的困肩,但是在JS端需要添加對應(yīng)的JS,對于前者脆侮,還需要把響應(yīng)的
方法放到橋梁內(nèi)锌畸,如:
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge)
}, false)
}
}
connectWebViewJavascriptBridge(function(bridge) {? ? ?
?var uniqueId = 1? ? ?
?function log(message, data) {? ? ? ?
?? var log = document.getElementById('log')? ?
? ?var el = document.createElement('div')??
? ?el.className = 'logLine'??
? ?el.innerHTML = uniqueId++ + '. ' + message + ':' + JSON.stringify(data)? ? ?
? ?if (log.children.length) {
?log.insertBefore(el, log.children[0])?
} ?else { log.appendChild(el) }
?}
bridge.init(function(message, responseCallback) {
log('JS got a message', message)
var data = { 'Javascript Responds':'Wee!' }
log('JS responding with', data)
responseCallback(data)
})
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
var button = document.getElementById('buttons').appendChild(document.createElement('button'))
button.innerHTML = 'Send message to ObjC'
button.onclick = function(e) {
e.preventDefault()
var data = 'Hello from JS button'
log('JS sending message', data)
bridge.send(data, function(responseData) {
log('JS got response', responseData)
})
}
document.body.appendChild(document.createElement('br'))
var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.onclick = function(e) {
e.preventDefault()
log('JS calling handler "testObjcCallback"')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
}
})
connectWebViewJavascriptBridge
這個(gè)方法是必須的,而響應(yīng)要放在這個(gè)方法中靖避,這樣對安卓端可能會中影響潭枣,于是放棄了這個(gè)庫的使用比默。
將下來是使用OVGap這個(gè)庫。
使用這個(gè)庫前卸耘,需要給HTML5中引入對方的腳本退敦,叫ovgap.js,可到Github下載:
;(function() {
var require, define;
(function () {
var modules = {},
// Stack of moduleIds currently being built.
requireStack = [],
// Map of module ID -> index into requireStack of modules currently being built.
inProgressModules = {},
SEPERATOR = ".";
function build(module) {
var factory = module.factory,
localRequire = function (id) {
var resultantId = id;
//Its a relative path, so lop off the last portion and add the id (minus "./")
if (id.charAt(0) === ".") {
resultantId = module.id.slice(0, module.id.lastIndexOf(SEPERATOR)) + SEPERATOR + id.slice(2);
}
return require(resultantId);
};
module.exports = {};
delete module.factory;
factory(localRequire, module.exports, module);
return module.exports;
}
require = function (id) {
if (!modules[id]) {
throw "module " + id + " not found";
} else if (id in inProgressModules) {
var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
throw "Cycle in require graph: " + cycle;
}
if (modules[id].factory) {
try {
inProgressModules[id] = requireStack.length;
requireStack.push(id);
return build(modules[id]);
} finally {
delete inProgressModules[id];
requireStack.pop();
}
}
return modules[id].exports;
};
define = function (id, factory) {
if (modules[id]) {
throw "module " + id + " already defined";
}
modules[id] = {
id: id,
factory: factory
};
};
define.remove = function (id) {
delete modules[id];
};
define.moduleMap = modules;
})();
define("ov_gap", function(require, exports, module) {
var ovGap = {
callbackId: Math.floor(Math.random() * 2000000000),
callbacks: {},
commandQueue: [],
groupId: Math.floor(Math.random() * 300),
groups: {},
listeners: {},
invoke: function(cmd, params, onSuccess, onFail) {
if(!cmd) cmd = "defaultCommand";
if(!params) params = {};
this.callbackId ++;
this.callbacks[this.callbackId] = {
success: onSuccess,
fail: onFail
};
var rurl = "ovgap://" + cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;
document.location = rurl;
},
dispatchCommand: function(cmd, params, onSuccess, onFail) {
if(!cmd) cmd = "defaultCommand";
if(!params) params = {};
this.callbackId ++;
this.callbacks[this.callbackId] = {
success: onSuccess,
fail: onFail
};
var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;
this.commandQueue.push(command);
},
fetchNativeCommands: function() {
var json = JSON.stringify(this.commandQueue);
this.commandQueue = [];
return json;
},
activate: function() {
document.location = "ovgap://ready";
},
// return group ID
createGroup: function() {
this.groupId ++;
this.groups[this.groupId] = [];
return this.groupId;
},
dispatchCommandInGroup: function(cmd, params, onSuccess, onFail, groupId) {
if (!this.groups[groupId]) return false;
if(!cmd) cmd = "defaultCommand";
if(!params) params = {};
this.callbackId ++;
this.callbacks[this.callbackId] = {
success: onSuccess,
fail: onFail
};
var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;
this.groups[groupId].push(command);
return true;
},
activateGroup: function(groupId) {
if (!this.groups[groupId]) return false;
document.location = "ovgap://group/" + groupId;
},
fetchNativeGroupCommands: function(groupId) {
if (!this.groups[groupId]) return [];
var json = JSON.stringify(this.groups[groupId]);
this.groups[groupId] = [];
return json;
},
callbackSuccess: function(callbackId, params) {
try {
ovGap.callbackFromNative(callbackId, params, true);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = " + e);
}
},
callbackError: function(callbackId, params) {
try {
ovGap.callbackFromNative(callbackId, params, false);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = " + e);
}
},
callbackFromNative: function(callbackId, params, isSuccess) {
var callback = this.callbacks[callbackId];
if (callback) {
if (isSuccess) {
callback.success && callback.success(callbackId, params);
} else {
callback.fail && callback.fail(callbackId, params);
}
delete ovGap.callbacks[callbackId];
};
},
addGapListener: function(listenId, onSuccess, onFail) {
if (!listenId || !onSuccess || !onFail) return;
this.listeners[listenId] = {
success : onSuccess,
fail : onFail
};
},
removeListener: function(listenId) {
if (!this.listeners[listenId]) return;
this.listeners[listenId] = null;
},
triggerListenerSuccess: function(listenId, params) {
if (!this.listeners[listenId]) return;
var listener = this.listeners[listenId];
listener.success && listener.success(listenId, params);
},
triggerListenerFail: function(listenId, params) {
if (!this.listeners[listenId]) return;
var listener = this.listeners[listenId];
listener.fail && listener.fail(listenId, params);
}
};
module.exports = ovGap;
});
window.ov_gap = require("ov_gap");
}) ();
給按鈕添加一個(gè)點(diǎn)擊事件蚣抗,回調(diào)如下:
[javascript] view plain copy print?在CODE上查看代碼片派生到我的代碼片
function onButtonClick() {
// 下面是我需要處理的事侈百,處理完之后
alert('這里我是要處理一些事的,如果有需要的話翰铡。');
// 這里是回調(diào)我們前端與后端商量好的方法
// activityList是oc中的方法
window.ov_gap.invoke("activityList", null, success, fail);
}
這樣就從JS回調(diào)到了IOS端的OC方法钝域,然后處理我們想做的事《В可是這兩個(gè)庫都需要添加這些東西例证,做HTML5的人可不愿意,因?yàn)檫@樣的話迷捧,對于iOS的處理是一種织咧,對于安卓和WP呢?又得寫一份嗎漠秋?于是我又去尋找別的庫笙蒙,有一個(gè)叫apache cordova的庫,是支持ios,Android,wp的庆锦,可是太大了捅位,又是英文的,安裝也很困難搂抒,學(xué)習(xí)成本太高艇搀,于是看了看就放棄了,這么大的庫求晶,給我們帶來的可不一定是好處多于動壞處啊焰雕。轉(zhuǎn)了一圈又回到了原生的,有一個(gè)庫叫JavaScriptCore芳杏,這個(gè)是IOS7以后才開放的API淀散,這可以極大的簡化了我們的需求,非常的簡單蚜锨,我們只需要注入一個(gè)方法,就可以在JS中調(diào)用此方法來跟原生的OC交互慢蜓。首先得加入庫[javascript] view plain copy print?在CODE上查看代碼片派生到我的代碼片#importJSContext這個(gè)可是關(guān)鍵亚再。
[objc] view plain copy print?在CODE上查看代碼片派生到我的代碼片
// webView對象
@property (nonatomic, strong, readonly) UIWebView? ? *webView;
@property (nonatomic, strong, readonly) JSContext? ? *jsContext;
下面是在webview加載完成后, 關(guān)聯(lián)JS與OC:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[_activityView stopAnimating];
[self dismiss];
if (_jsContext == nil) {
// 1.
_jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 2. 關(guān)聯(lián)打印異常
_jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
DDLogVerbose(@"異常信息:%@", exceptionValue);
};
_jsContext[@"activityList"] = ^(NSDictionary *param) {
DDLogVerbose(@"%@", param);
};
// Mozilla/5.0 (iPhone; CPU iPhone OS 10_10 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B411
id userAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
DDLogVerbose(@"%@", userAgent);
}
}
上面activityList是商定好的方法名稱晨抡,在JS中寫法:[javascript] view plain copy print?在CODE上查看代碼片派生到我的代碼片在點(diǎn)擊的時(shí)候氛悬,直接回調(diào)是可以的则剃。那么經(jīng)過這兩天的摸索,學(xué)習(xí)到了很多的知識如捅。寫DEMO的過程中棍现,由于 后臺并沒有提供好HTML5頁面的交互來測試,需要自己寫镜遣,我這里是使用apache服務(wù)器己肮,在本地創(chuàng)建一個(gè)HTML5頁面,自己寫一些JS來測試的悲关,如果大家不知道怎么寫JS谎僻,其實(shí)是很簡單的,上w3cschool看一看寓辱,就明白了艘绍,so easy!!!!之所以要寫下這篇文章,是因?yàn)槲野l(fā)現(xiàn)在網(wǎng)上搜索出來的文章中秫筏,都是相互復(fù)制的诱鞠,看來看去都是一樣的東西而且還都是OC調(diào)JS的代碼,實(shí)在是不快这敬。寫下的點(diǎn)滴航夺,希望對大家有用。另外鹅颊,也許我上面所講的一些關(guān)于OVGap和WebviewJavaScriptBridge的知識是有不正確的地方敷存,還請指出來,你們的反饋堪伍,會是對我最大的幫助锚烦,謝謝!