最近在做的項目重點就是原生app與js的交互,以前也做過但是并沒有深入的了解和研究過,因為這個項目我嘗試了三種方式的交互方法,經過實踐也更了解每種方法的利弊,再次分享一下希望大家可以批評指正:
首先分析一下app目前的樣式分類原生app和webapp的定義以及優(yōu)缺點Web App
WebApp
即是一種框架型APP開發(fā)模式(HTML5 APP框架開發(fā)模式),該開發(fā)具有跨平臺的優(yōu)勢,該模式通常由“HTML5云網(wǎng)站+APP應用客戶端”兩部份構成,APP應用客戶端只需安裝應用的框架部份,而應用的數(shù)據(jù)則是每次打開APP的時候,去云端取數(shù)據(jù)呈現(xiàn)給手機用戶。
原生App
原生APP又稱Native
App,該開發(fā)針對IOS、Android、Windows等不同的手機操作系統(tǒng)要采用不同的語言和框架進行開發(fā),該模式通常是由“云服務器數(shù)據(jù)+APP應用客戶端”兩部份構成,APP應用所有的UI元素、數(shù)據(jù)內容、邏輯框架均安裝在手機終端上。
Hybrid App(混合模式移動應用)
是指介于web-app、native-app這兩者之間的app,兼具“Native App良好用戶交互體驗的優(yōu)勢”和“Web App跨平臺開發(fā)的優(yōu)勢”。
這篇文章作者總結的不錯
http://www.oschina.net/question/585173_2141646
這篇文章講述了目前移動開發(fā)的趨勢:
為什么移動開發(fā)開始用混合app開發(fā)(Hybrid App)
知道了各種形式app的定義卻別,也更了解Hybrid App的優(yōu)勢那怎樣更好的實現(xiàn)原生與web 的交互呢?
首先我認為最好的方式
1. 運用JavaScriptCore框架進行交互
在iOS 7之后,apple添加了一個新的庫JavaScriptCore,用來做JS交互,因此JS與原生OC交互也變得簡單了許多。這是我做的一個例子
1.首先創(chuàng)建JSContext 對象(此處通過當前webView的鍵獲取到jscontext)
_mJSContext=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
2.異常處理
_mJSContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
DLog(@"%@", exceptionValue);
};
3.在JS中添加了方法指針提供給JS調用
@WeakObj(self);
_mJSContext[@"jSInvokerGetMid"] = ^(void){
DLog(@"JS獲取UUID");
@StrongObj(self);
return ?self.devie.mac;
};
_mJSContext[@"jSInvokerGetLocation"] = ^(void){
DLog(@"JS獲取坐標");
@StrongObj(self);
id devicePoint = @{
@"latitude": [NSString stringWithFormat:@"%f", self.location.coordinate.latitude],
@"longitude": [NSString stringWithFormat:@"%f", self.location.coordinate.longitude]
};
return devicePoint;
};
_mJSContext[@"jSInvokerGetUserSgid"] = ^(void){
DLog(@"JS用戶的ID");
return @"UserId";
};
_mJSContext[@"jSInvokerSpeechSubView"] = ^(void){
DLog(@"將要進行界面跳轉由h5界面跳轉到原生界面");
@StrongObj(self);
NSArray *args = [JSContext currentArguments];
JSValue * value = args[0];
NSString * str = value.toString;
//必須放入主線程中更新UI否則會出錯
dispatch_async(dispatch_get_main_queue(), ^{
SGHomeSeachDetailController * detailViewCon = [[SGHomeSeachDetailController alloc]init];
detailViewCon.baseUrl = str;
[self.navigationController pushViewController: detailViewCon animated:YES];
});
};
4.iOS調用JS進行初始化
NSString *textJS = @"window.ios_callback('這里是JS中alert彈出的message')";
[_mJSContext evaluateScript:textJS];
也可以參考:IOS中 使用JavaScriptCore 實現(xiàn)OC與JS的交互 - 簡書
以及我的博客:JS和Native交互之 - 運用JavaScriptCore框架進行交互 - lxm_780337的博客? ? ? ? ?- 博客頻道 - CSDN.NET
WebViewJavascriptBridge
Obj-C和JavaScript原理:
Obj-C調用JavaScript很簡單,可以通過webview的stringByEvaluatingJavaScriptFromString:方法調用JavaScript代碼;JavaScript調用Obj-C,則是通過web
view的代理方法shouldStartLoadWithRequest:來接收JavaScript的網(wǎng)絡請求從而實現(xiàn)調用。
利弊:
需要把響應的方法放到橋梁內,這樣對安卓端可能會中影響
不錯的例子:
1.按照本地的CSS文件展示一串網(wǎng)絡獲取的帶HTML格式的只有body部分的文本,需要自己拼寫完整的HTML。除此之外,還需要禁用獲取的HTML文本中自帶的
《 img 》
標簽自動加載,并把下載圖片的操作放在native端來處理,并通過JS將圖片在Cache中的地址返回給UIWebview。http://www.cocoachina.com/ios/20150814/12985.html
過程:
一開始,我們在Native端和JS端都分別進行初始化:
OC端:
@property WebViewJavascriptBridge* bridge;
對應的初始化代碼如下,在初始化中直接包含了一個用于接收JS的回調:
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"ObjC received message from JS: %@", data);
responseCallback(@"Response for message from ObjC");
}];
JS端:(以下是固定寫法,你自己的JS文件中必須包含如下代碼)
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge)
}, false)
}
connectWebViewJavascriptBridge(function(bridge) {
//注意,你的bridge函數(shù)都應寫在這里面
}
//例如
function doSomething(){
connectWebViewJavascriptBridge(function(bridge) {
bridge.init(function(message, yourCallback) {
log('ObjC called testJavascriptHandler with', message)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
}
}
然后,我們要知道,在WebViewJavascriptBridge中,交互的方式只有兩種:send 和 callHandle,JS和OC都有這兩個方法,所以對應的四種關系是:
blob.png
OC調用JS方法
1.調用JS中bridge.init的方法
//OC
[_bridge send:@"The message sent from ObjC to JS" responseCallback:^(id response) {
NSLog(@"sendMessage got response: %@", response);//這里的response是js方法中的data
}];
//對應調用js中的方法
bridge.init(function(message, yourCallback) {
log('ObjC called testJavascriptHandler with', message)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
2.調用JS中bridge.registerHandler方法,該方法可以注冊方法名,通過注冊名確認調用方法
//OC
[_bridge callHandler:@"注冊名" data:data responseCallback:^(id response) {
NSLog(@"testJavascriptHandler responded: %@", response);
}];
//對應調用的js中的方法
bridge.registerHandler('注冊名', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
JS端調用OC端方法
1.調用OC中創(chuàng)建bridge對象時定義時的方法
//JS中
bridge.send(data,function(response){
log('這里是回調方法',response) //回調方法
})
//對應調用的OC中bridge初始化中設置的block方法
_bridge
= [WebViewJavascriptBridge bridgeForWebView:webView
webViewDelegate:self handler:^(id data, WVJBResponseCallback
responseCallback) {
//do something...
NSLog(@"ObjC received message from JS: %@", data);//從js獲得的參數(shù)
responseCallback(@"Response");//Response to js
}];
2.通過方法名調用OC中bridge的注冊方法
//JS中
bridge.callHandler('注冊名', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
//對應調用的OC中的bridge注冊的方法
[_bridge registerHandler:@"注冊名" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);//從js獲取的參數(shù)
responseCallback(@"Response");//
}];
這篇文章講了交互原理非常不錯:JS和Native交互之 -WebViewJavascriptBridge源碼分析 - lxm_780337的博客? ? ? ? ?- 博客頻道 - CSDN.NET
UIWebView的代理方法
原理:- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType;在WebView中的wap頁將要載入內容時得到通知觸發(fā),返回NO則阻止加載內容,優(yōu)點:簡單易用,在頁面跳轉請求時進行iOS端的頁面跳轉,并返回NO阻止wap內的頁面跳轉缺點:無法識別不加載新內容的JS動作(點擊按鈕彈出對話框和提交等)例子:HTML網(wǎng)頁和一個btn點擊事件用來與原生OC交互(用JS發(fā)起一個假的URL請求,然后利用UIWebView的代理方法攔截這次請求,然后再做相應的處理)JS調用IOSHTML代碼如下: