iOS-JSBrigde:JS和Native通訊實現的兩種方式
現在純原生的APP是越來越少了,更多的都加入了Web混合開發(有UiWebView和iOS 8之後的WKWebView,後者在效能上優化了許多),甚至有些直接是WebAPP。JSPatch、weex以及ReactNative等熱更新技術也相當流行,雖然2017年3月份蘋果的一封郵件警告,讓許多使用者對這些熱更新技術有點害怕。
既然用到了web,就避免不了JS和OC語言的互相呼叫。
本文介紹自己專案中的js-oc互相呼叫實現方法。從一開始簡單的url攔截到最近的基於JavaScriptcore建立的JSbrige兩套方案。
URL攔截方式
JS呼叫OC:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString * currentUrl = webView.request.URL.absoluteString;
//判斷是否是單擊
if (navigationType == UIWebViewNavigationTypeLinkClicked)
{
NSURL *url = [request URL];
NSLog(@"當前url%@",url);
NSString *urlStr = url.absoluteString;
if ([[url scheme] isEqualToString:@"cloudsc6"]){
if ([[url host] isEqualToString:@"PLAYVOICE"]) {
NSLog(@"準備播放語音訊息");
}
}
else {
return YES;
}
}
return YES;
}
這個實現方式事實上有很多侷限,首先引數的傳遞就不太友好,引數放在url中實現實在不是很優雅的實現方式。
而且,工作中實測,當JS實現事件的方式是Ajax非同步的時候,有些事件這個代理是攔截不到的。
當時解決的方案是使用了NSURLProtocol全域性去攔截url請求。如下
@implementation InnerResourcesURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
BOOL can = NO;
//NSLog(@"--->can handled Request? #%lu: URL = %@", (unsigned long)requestCount++, request);
// NSLog(@"%@",request);
if([NSURLProtocol propertyForKey:CLOUDSEE_INNER_PROTOCOL_REQ_HANDLED_KEY inRequest:request]){
can = NO;
}else {
NSURL *url = [request URL];
if(([[url scheme] isEqualToString:DEFAULT_SCHEMA])||([[url scheme]isEqualToString:@"th"])){
can = YES;
}
}
return can;
}
- (void) startLoading
{
AppDelegate *dd = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSURL *url = [self.request URL];
if ([[url scheme] isEqualToString:@"PLAYVOICE"]) {
}
}
同樣地也有其侷限性。
OC呼叫JS
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *scriptResult = [self.webView stringByEvaluatingJavaScriptFromString:@"didWebViewScroll()"];
if([scriptResult isEqualToString:@"NO"]) {
self.webView.scrollView.bounces=NO;
}
}
這個實現方式簡單,但是同樣也相當不友好。首先傳引數在didWebViewScroll()中的()實現,不僅對長度有限,而且如果引數有含有''會導致擷取錯誤程式碼。
而且這個方案下,有些事件安卓能擷取,iOS 不能擷取。有些事件iOS 能擷取,安卓不能擷取。跨平臺性也不好。
WebViewJavascriptBridge
網上有一個著名的庫,星星數已達8823。但是其底層實現原理還是基於URL攔截方式去實現。
再次證明星星數不能代表什麼,只能說其比較出名。
JavaScriptcore橋接實現
如果你的APP不需要支援iOS 7之前的版本,可以使用以下方案,我們公司現在用的就是這個方案。
蘋果在iOS 7中加入了JavaScriptCore框架。該框架讓Objective-C和JavaScript程式碼直接的互動變得更加的簡單方便。
橋接關鍵 和JS呼叫OC
-(void)webViewDidFinishLoad:(UIWebView *)webView{
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//列印JS異常
[self.jsContext setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"oc catches the exception: %@", value);
}];
//以下是橋接JS與OC的關鍵
JSModel * model = [[JSModel alloc]init];
model.controller = self;
model.JSContext = self.jsContext;
//以下是H5中呼叫的方法名稱
NSString * key = @"DDJSBridge";
[self.jsContext setObject:model forKeyedSubscript:key];
JSValue * loadData = self.jsContext[@"loadData"];
//以下是回撥JS方法,可以傳入封裝好的引數
[loadData callWithArguments:@[self.jsonStr]];
if (isLoadData) {
[self firstIn];
}
isLoadData = NO;
// 以下是執行JS方法
// [self.jsContext evaluateScript:self.htmlCont];
}
OC呼叫JS
只要將JSModel繼承JSExport,再本地實現以下方法
-(void)call:(NSString *)methodName :(NSDictionary *)params :(JSValue *)callBack{
NSDictionary * data = [params objectForKey:@"data"];
if ([methodName isEqualToString:@"showShareButton"]) {
NSString * iconsStrUrl = [data objectForKey:@"icon"];
if (iconsStrUrl) {
NSURL * iconUrl = [NSURL URLWithString:iconsStrUrl];
NSData * imageData = [NSData dataWithContentsOfURL:iconUrl];
UIImage *image = [UIImage imageWithData:imageData scale:3];
UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc]
initWithImage:image
style:UIBarButtonItemStylePlain
target:self
action:@selector(rightClick)];
self.controller.navigationItem.rightBarButtonItem = rightBarButton;
}
}
至於跨平臺性,也比第一個方案要友好,安卓也有類似的實現方案。
第二個方案的DEMO已經上傳,可以下載
JSBrigeOC版本
JSBrigeSwift版本
JavascriptBridge實現內部原理
有一個百度團隊的部落格寫的很好,可以參考。
深入淺出 JavaScriptCore
淺談JavaScriptCore
阿里無限團隊介面實現規範
我們也是基本按照它的介面規範的
阿里無限團隊介面實現規範
Weex 是如何在 iOS 客戶端上跑起來的
相關文章
- Activity和Service跨程式通訊的兩種方式
- nginx與php-fpm通訊的兩種方式NginxPHP
- Native 工程整合Flutter 的兩種方式Flutter
- activemq的兩種基本通訊方式的使用及總結MQ
- 基於 Electron 做視訊會議的兩種實現方式
- Spring實現IOC容器的兩種實現方式Spring
- JS 垃圾回收的兩種方式JS
- 兩種方式實現輪播圖
- js實現繼承的幾種方式和對比JS繼承
- 簡單說 通過CSS實現 文字漸變色 的兩種方式CSS
- vue通訊的N種方式Vue
- 前端--實現隔行變色的兩種方式前端
- 圓形視訊和圓角視訊的一種實現方式
- Vue.js 父子元件通訊的1212種方式Vue.js元件
- JS 繼承的 六 種實現方式JS繼承
- js實現繼承的三種方式JS繼承
- js實現繼承的幾種方式JS繼承
- wpf兩種佔位符實現方式
- 兩種方式實現web html sliderWebHTMLIDE
- 兩種遞迴方式實現迴文字遞迴
- 驅動和應用層的三種通訊方式
- 程式間的幾種通訊方式
- 幾種程式間的通訊方式
- 程式間的8種通訊方式
- MyBatis中主鍵回填的兩種實現方式MyBatis
- Spring定時器的兩種實現方式Spring定時器
- 【jmeter】實現介面關聯的兩種方式:正規表示式提取器和json提取器JMeterJSON
- 兩個有名管道實現qq通訊
- 兩個視窗如何實現通訊
- CommonJS的兩種匯出方式JS
- ViewPager兩種方式實現無限輪播Viewpager
- SpringBoot實現熱部署兩種方式!Spring Boot熱部署
- 兩種方式實現橫向滾動條
- 主機和虛擬機器的三種通訊方式虛擬機
- zuul實現Cors跨域的兩種方式(https)ZuulCORS跨域HTTP
- Spring宣告式事務的兩種實現方式Spring
- 關於多執行緒的兩種實現方式執行緒
- React中元件通訊的幾種方式React元件