Hybrid App: 看看第三方WebViewJavascriptBridge是如何來實現Native和JavaScript交互

1、簡介javascript

在前面兩篇文章中已經介紹了Native與JavaScript交互的幾種方式,依次是JavaScriptCore框架、UI組件UIWebView、WebKit框架,這幾種方式都是蘋果公司提供的,除了UIWebView在IOS8以後被蘋果淘汰了外,其餘基本都能很好地知足開發者的使用。做爲一個技術人員,每一個人內心都有造輪子的想法,可能有時以爲原生的API使用起來感受仍是不夠方便,就對蘋果原生的API再進行一層封裝,這不WebViewJavascriptBridge這個輪子出來了。WebViewJavascriptBridge的star和fork量仍是比較高的,仔細看看WebViewJavascriptBridge類文件至關簡單,使用起來也很方便,很受開發者歡迎,它的原理仍是利用WKWebView或者UIWebView的相關API,經過bridge橋樑來實現OC與JS互相註冊和調用。大體結構圖以下:css

能夠看出:OC調用JS,JS須要註冊函數; JS調用OC,OC須要註冊函數。html


 

2、分析java

瞭解基本原理結構圖後,再來看看框架中的類以及它們的做用定義,以下:web

WebViewJavascriptBridge_JS:Javascript環境的Bridge初始化和處理。負責接收OC發給Javascript的消息,而且把Javascript環境的消息發送給OC。安全

WKWebViewJavascriptBridge/WebViewJavascriptBridge:主要負責OC環境的消息處理,而且把OC環境的消息發送給Javascript環境。app

WebViewJavascriptBridgeBase:主要實現了OC環境的Bridge初始化和處理。框架

//初始化橋接器
+ (instancetype)bridgeForWebView:(id)webView;
+ (instancetype)bridge:(id)webView;

//設置日誌相關
+ (void)enableLogging;
+ (void)setLogMaxLength:(int)length;

//註冊函數, handlerName: 函數名稱  handler:傳遞數據的block
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)removeHandler:(NSString*)handlerName;

//調用函數, handlerName:函數名稱 data:參數 responseCallback:接收數據的block 
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;

//設置網頁代理
- (void)setWebViewDelegate:(id)webViewDelegate;

//禁用超時安全彈框
- (void)disableJavscriptAlertBoxSafetyTimeout;

調用相關知識點 :dom

// OC 調用 JS
// 一、單純的調用 JSFunction。 [self.jsBridge callHandler:@"JSFunction"]; // 二、調用 JSFunction,並傳遞給js須要的參數,但不須要 JSFunciton 的返回值。 [self.jsBridge callHandler:@"JSFunction" data:"arg of js"]; // 三、調用 JSFunction ,並傳遞給js須要的參數,也須要 JSFunction 的返回值。 [self.jsBridge callHandler:@"JSFunction" data:"arg of js" responseCallback:^(id responseData) { NSLog(@"%@",responseData); }]; // ------------------------------------------------------------------------------- // //JS 調用 OC // 一、JS 單純的調用 OC 的 OCMethod WebViewJavascriptBridge.callHandler('OCMethod'); // 二、JS 調用 OC 的 OCMethod,並傳遞給OC須要的參數 WebViewJavascriptBridge.callHandler('OCMethod',"arg of oc"); // 三、JS 調用 OC 的 OCMethod,傳遞給OC須要的參數,並接受OC的返回值。 WebViewJavascriptBridge.callHandler('OCMethod', "arg of oc", function(responseValue){ alert(responseValue);
});

 

3、核心ide

使用該框架,還須要完成某些初始化的工做,也即在HTML或者JavaScript文件中,拷貝進官方指定的函數,在函數內進行初始化操做:

//固定格式的函數
function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

//官方說這裏主要進行初始化,有兩個功能:一、進行JS函數的註冊提供OC調用(必須在此實現)   二、JS調用OC在端上註冊的函數(不用在此處實現)
//這裏須要說明一下: 至於第2點若是放在這裏會當即執行且執行一次。能夠挪到某一個js事件中執行,例如按鈕事件等,能夠頻繁觸發,後面的使用會有demo演示
setupWebViewJavascriptBridge(function(bridge) {
    
    /* Initialize your app here */

    bridge.registerHandler('JavaScript_functionName', function(data, responseCallback) {
        responseCallback(data); //傳遞數據給OC
    })


    bridge.callHandler('OC_methodName', function responseCallback(responseData) {
        alert(responseData); //接收OC的數據
    })
})    

 

4、使用

Example introduce:  頁面有一個原生按鈕和H5按鈕,點擊原生按鈕調用JS,切換H5的div背景色;點擊H5按鈕,調用OC方法,切換原生按鈕背景顏色。

完整代碼:

//
//  ViewController.m
//  WebViewJavaScriptBridge
//
//  Created by 夏遠全 on 2019/11/17.
//  Copyright © 2019 Beijing Huayue Education Technology Co., Ltd. All rights reserved.
//

#import "ViewController.h"
#import "WebViewJavascriptBridge/WebViewJavascriptBridge.h"

@interface ViewController ()
@property (nonatomic, strong) WebViewJavascriptBridge *bridge;
@property (nonatomic, strong) UIButton  *topButton;
@property (nonatomic, strong) WKWebView *wkWebView;
@end

@implementation ViewController

#pragma mark - init

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //建立topView
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    self.topButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, 200)];
    self.topButton.titleLabel.numberOfLines = 0;
    self.topButton.backgroundColor = [UIColor blueColor];
    self.topButton.titleLabel.font = [UIFont systemFontOfSize:17];
    [self.topButton setTitle:@"我是TopButton\n點擊我調用JS方法,切換div盒子的背景色" forState:UIControlStateNormal];
    [self.topButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.topButton addTarget:self action:@selector(topButtonAction) forControlEvents:UIControlEventTouchUpInside];
    

    //建立wkWebView
    CGFloat height = [UIScreen mainScreen].bounds.size.height-CGRectGetHeight(self.topButton.frame);
    self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.topButton.frame), width, height)];
    
    //添加wkWebView視圖
    [self.view addSubview:self.topButton];
    [self.view addSubview:self.wkWebView];
    
    //爲wkWebView建立橋接器
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wkWebView];
    
    //OC註冊方法,提供給JavaScript調用,並給JavaScript傳遞數據
    [self.bridge registerHandler:@"updateTopButtonBgColor" handler:^(id data, WVJBResponseCallback responseCallback) {
        UIColor *randomColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1.0];
        self.topButton.backgroundColor = randomColor;
        [self showAlertView:@"JavaScript調用OC ----- success! "];
        responseCallback(@"JavaScript調用OC ----- success! "); //能夠回調給JavaScript一個結果
    }];
    
    //加載資源
    NSString *file = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"html"];
    NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    [self.wkWebView loadHTMLString:html baseURL:nil];
}

#pragma mark - remove
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.bridge removeHandler:@"updateTopButtonBgColor"];
}

#pragma mark -action
-(void)topButtonAction {
    
    //調用JavaScript函數,傳遞顏色參數,並接收JavaScript回傳的數據
    NSArray *colors = [NSArray arrayWithObjects:@"orange",@"green",@"red",@"blue",nil];
    [self.bridge callHandler:@"updateDivBgColor" data:colors[arc4random_uniform(4)] responseCallback:^(id responseData) {
        [self showAlertView:responseData];
    }];
}

#pragma mark - method
-(void)showAlertView:(NSString *)message {
    UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil];
    [aletVc addAction:action];
    [self presentViewController:aletVc animated:YES completion:nil];
}


@end
View Code

細分步驟:

一、建立HTML,完成初始工做

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  
  <style>
      .divcss{ background:#F00; color:#FFF; width:300px; height:200px}
  </style>
  
  <script type="text/javascript">
      
    function button_onclick(){
        //JavaScript調用OC註冊的方法,並接收OC的數據
        WebViewJavascriptBridge.callHandler('updateTopButtonBgColor',function(responseValue) {
            alert(responseValue);
        });
    }

      // 固定函數 
    function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
    
    // 初始化
    setupWebViewJavascriptBridge(function(bridge){
       
         //JavaScript調用OC註冊的方法,並接收OC的數據,頁面啓動後會當即調用且調用一次,我把它放到了上面的👆button_onclick()事件中能夠頻繁執行
         //WebViewJavascriptBridge.callHandler('changeTopButtonBgColor',function(responseValue) {
             //alert(responseValue);
         //});
          
         //JavaScript註冊函數,提供給OC調用,並傳遞數據給OC
         bridge.registerHandler('updateDivBgColor',function(colorData, responseCallback) {
             alert(colorData);
             document.getElementById("div").style.backgroundColor = colorData;
             responseCallback("OC調用JavaScript ----- success! "); 
         });
    });

  </script>
  
</head>

<body>
    <div class="divcss" id="div">
        <br>
        <h2 style="text-align:center"> div盒子 </h2>
    </div>
    
    <br>
    <button onclick="button_onclick()">點擊調用OC方法,切換TopButton的背景色</button>
    
</body>

</html>

二、導入頭文件,定義控件屬性

//頭文件
#import "ViewController.h"
#import "WebViewJavascriptBridge/WebViewJavascriptBridge.h"

//定義屬性
@interface ViewController ()
@property (nonatomic, strong) WebViewJavascriptBridge *bridge;
@property (nonatomic, strong) UIButton  *topButton;
@property (nonatomic, strong) WKWebView *wkWebView;
@end 

三、建立控件,並添加到父視圖

//建立topView
CGFloat width = [UIScreen mainScreen].bounds.size.width;
self.topButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, 200)];
self.topButton.titleLabel.numberOfLines = 0;
self.topButton.backgroundColor = [UIColor blueColor];
self.topButton.titleLabel.font = [UIFont systemFontOfSize:17];
[self.topButton setTitle:@"我是TopButton\n點擊我調用JS方法,切換div盒子的背景色" forState:UIControlStateNormal];
[self.topButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.topButton addTarget:self action:@selector(topButton) forControlEvents:UIControlEventTouchUpInside];
    
//建立wkWebView
CGFloat height = [UIScreen mainScreen].bounds.size.height-CGRectGetHeight(self.topButton.frame);
self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.topButton.frame), width, height)];

//添加wkWebView視圖
[self.view addSubview:self.topButton];
[self.view addSubview:self.wkWebView];

四、建立橋接器

//爲wkWebView建立橋接器
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wkWebView]; 

五、JavaScript調用OC: 須要OC註冊方法

//OC註冊方法,提供給JavaScript調用,並給JavaScript傳遞數據
[self.bridge registerHandler:@"changeTopButtonBgColor" handler:^(id data, WVJBResponseCallback responseCallback) {
    UIColor *randomColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1.0];
    self.topButton.backgroundColor = randomColor;
    [self showAlertView:@"JavaScript調用OC ----- success! "];
    responseCallback(@"JavaScript調用OC ----- success! "); //能夠回調給JavaScript一個結果
}];

六、OC調用JavaScript:須要在HTML中註冊JS函數

//原生按鈕事件
-(void)topButtonAction {
    //調用JavaScript函數,傳遞顏色參數,並接收JavaScript回傳的數據
    NSArray *colors = [NSArray arrayWithObjects:@"orange",@"green",@"red",@"blue",nil];
    [self.bridge callHandler:@"updateDivBgColor" data:colors[arc4random_uniform(4)] responseCallback:^(id responseData) {
        [self showAlertView:responseData];
    }];
}

七、加載HTML資源

//加載資源
NSString *file = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"html"];
NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
[self.wkWebView loadHTMLString:html baseURL:nil];

八、清除工做

//須要清除註冊到橋接器中的函數,不然致使vc沒法釋放
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.bridge removeHandler:@"updateTopButtonBgColor"];
}

九、顯示結果以下:能夠發現TopButton和div的背景色都能頻繁改變

相關文章
相關標籤/搜索