iOS與Javascript交互實(shí)戰(zhàn)

因?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的知識是有不正確的地方敷存,還請指出來,你們的反饋堪伍,會是對我最大的幫助锚烦,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帝雇,一起剝皮案震驚了整個(gè)濱河市涮俄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尸闸,老刑警劉巖彻亲,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吮廉,居然都是意外死亡苞尝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門宦芦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宙址,“玉大人,你說我怎么就攤上這事调卑÷丈埃” “怎么了大咱?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長注益。 經(jīng)常有香客問我碴巾,道長,這世上最難降的妖魔是什么丑搔? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任厦瓢,我火速辦了婚禮,結(jié)果婚禮上低匙,老公的妹妹穿的比我還像新娘旷痕。我一直安慰自己,他們只是感情好顽冶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布欺抗。 她就那樣靜靜地躺著,像睡著了一般强重。 火紅的嫁衣襯著肌膚如雪绞呈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天间景,我揣著相機(jī)與錄音佃声,去河邊找鬼。 笑死倘要,一個(gè)胖子當(dāng)著我的面吹牛圾亏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播封拧,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼志鹃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泽西?” 一聲冷哼從身側(cè)響起曹铃,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捧杉,沒想到半個(gè)月后陕见,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡味抖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年评甜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔涩。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忍坷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情承匣,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布锤悄,位于F島的核電站韧骗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏零聚。R本人自食惡果不足惜袍暴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隶症。 院中可真熱鬧政模,春花似錦、人聲如沸蚂会。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胁住。三九已至趁猴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彪见,已是汗流浹背儡司。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留余指,地道東北人捕犬。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像酵镜,于是被迫代替她去往敵國和親碉碉。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本教程中所涉及到的幾種類型: JSContext, JSContext是代表JS的執(zhí)行環(huán)境笋婿,通過-evaluate...
    貝勒老爺閱讀 858評論 0 5
  • 單例模式 適用場景:可能會在場景中使用到對象誉裆,但只有一個(gè)實(shí)例,加載時(shí)并不主動創(chuàng)建缸濒,需要時(shí)才創(chuàng)建 最常見的單例模式足丢,...
    Obeing閱讀 2,056評論 1 10
  • body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoot...
    wangyw閱讀 575評論 0 0
  • 在地球上,先有石頭庇配,后有人類斩跌,或者說沒有石頭就沒有人類±袒牛考古學(xué)對早期人類歷史分期的第一個(gè)時(shí)代就是石器時(shí)代耀鸦,從出現(xiàn)人...
    棉花兔閱讀 272評論 0 2
  • 這篇文章簡單的描述UICollectionView的基本使用方法,供需要簡單瀑布流布局的同學(xué)查看。 UIColle...
    阿猿閱讀 1,121評論 0 4