抖音-JSBridge


抖音 JSBridge(舊版,現在統一使用via)
 
 
一、JSBridge是什么
  1. 主流App開發模式
  • Native App:傳統原生APP開發模式。Android基於Java語言,底層調用Google的API,iOS基於OC或者Swift語言,底層調用ios官方提供的API。
  • Web App:網站開發模式。將頁面部署在服務器上,用戶使用瀏覽器訪問,一般泛指 SPA(Single Page Application)模式開發出的網站。
  • Hybrid App:半Native半web混合開發模式。介於Web App、Native App兩者之間,兼具Native良好交互體驗和Web頁跨平台開發優勢。
  • React Native App:用JS寫出的原生應用。

  2.Native vs Web

 
Native
Web
優點
可以調用系統底層API 交互順暢,轉場平滑 數據安全,穩定
跨多平台,表現力強 迭代快 即時上線,無需發版
缺點
任何改動需要發版(安卓熱更新除外) 兩端各一套實現方式,無法跨平台開發
無法調用系統底層API 性能取決於容器 安全性穩定性相對較弱
 
 
Native App
Web App
Hybrid App
React Native App
原生功能體驗
優秀
良好
接近優秀
渲染性能
非常快
接近快
是否支持設備底層訪問
支持
不支持
支持
支持
網絡要求
支持離線
依賴網絡
支持離線(資源存本地情況)
支持離線
更新復雜度
高(幾乎總是通過應用商店更新)
低(服務器端直接更新)
較低(可以進行資源包更新)
較低(可以進行資源包更新)
編程語言
Android(Java) iOS(OC/Swift)
js+html+css3
js+html+css3
主要使用JS編寫 語法規則JSX
社區資源
豐富
豐富(大量前端資源)
有局限(不同的Hybrid相互獨立)
豐富(統一的活躍社區)
上手難度
難(不同平台需要單獨學習)
簡單(寫一次,支持不同平台訪問)
簡單(寫一次,運行任何平台)
中等(學習一次,寫任何平台)
開發周期
較短
中等
開發成本
昂貴
便宜
較為便宜
中等
跨平台
不跨平台
所有H5瀏覽器
Android,iOS,h5瀏覽器
Android,iOS
APP發布
App Store
Web服務器
App Store
App Store

  3.Hybrid App

  • 定義:Hybrid App(混合模式開發的移動應用)底層功能API均由原生容器通過某種方式提供,業務邏輯由H5頁面完成,最后原生容器加載H5,從而完成整個業務流程,只需要寫一套代碼即可,即可達到跨平台效果。
  • 原生容器 :Native中的webview組件,用於加載HTML文件。Android中是webview,iOS7以下有UIWebview,iOS7以上有了WKWebview。
  • hybrid架構:上層為web層,底層為native層,通信靠jsbridge

  4.hybrid核心--JSBridge技術:構建了 Native 和非 Native 間消息雙向通信的通道

  • JS 向 Native 發送消息 : 調用相關功能、通知 Native JS當前狀態。
  • Native 向 JS 發送消息 : 回溯調用結果、消息推送、通知 JS 當前 Native 的狀態。
 
二、JSBridge實現原理
JSBridge是一種通用的交互理念,多種設計方式都可以實現,思路也不盡相同。
  1. JS 調用 Native 的幾種通信方案
    • 特殊url scheme假跳轉的請求攔截:h5發出一條跳轉請求,其中跳轉目的地是一個非法的不存在的地址,客戶端攔截並分析請求從而調用native方法。
    • 優點: 兼容性好。這是所有JS調用Native的通信方式里,唯一同時支持安卓webview/蘋果UIWebView/WKWebView的通信方式。
    • 缺點:webview會把調用封裝為請求,時延達到200ms~400ms;跳轉的URL存在長度限制。
    • JS上下文注入API:通過 WebView 提供的接口,向JS運行的Context(window)中直接注入對象或者方法,JavaScript調用時能直接執行相應的Native代碼邏輯。
    • 優點: 由於同步返回調用速度非常快,參照alert。
    • 缺點: 低版本iOS系統不支持此方式;安卓 4.2 之前,注入JavaScript的接口是 addJavascriptInterface,存在安全漏洞,4.2 后引入JavascriptInterface新接口做替代,所以存在兼容性問題。
 

  2.請求攔截假跳轉通信方式詳解

  • url地址分為幾部分:
  協議://域名/路徑?參數 
  aweme://profile/?douyin_id=88 (假跳轉)
 
  • JS發起跳轉3種方式:
  1)在HTML中用a標簽直接填寫假請求地址
  <a href="aweme://profile/?douyin_id=88">A標簽</a>
  2) 原地跳轉:在JS中用location.href
  location.href = 'aweme://profile/?douyin_id=88'
  3) iframe跳轉:在JS中創建一個iframe,插入dom之中進行跳轉
  $('body').append('<iframe src="' + 'aweme://profile/?douyin_id=88' + '" style="display:none"></iframe>');
 
  • 攔截規則:因為客戶端內打開H5頁面的webview容器,會無差別攔截h5頁面發送所有請求,真正的url地址會正常跳轉,而協議域名符合一定規則的url地址則會被客戶端攔截,攔截下來的url不會導致webview繼續跳轉,因此用戶完全沒有感知。我們可以利用這個條件,定義一些scheme規則,客戶端讀取偽協議域名的部分作為通信識別,如bytedance:// , snssdk1128://, aweme:// 可與正常協議做區分,讀取路徑作為指令識別,讀取參數作為數據,並根據約定調用對應的native原生代碼。
 
  • 客戶端請求攔截:不同種類webview通過不同方法,都實現了這樣的流程:首先根據協議/域名判斷是否需要攔截此調用,若需要則取出路徑,並匹配出指令,傳入js攜帶參數數據調用相應native方法,停止webview的繼續請求。
  • 1)安卓 shouldOverrideUrlLoading
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    //1 根據url,判斷是否是所需要的攔截的調用 判斷協議/域名
    if (是){
      //2 取出路徑,確認要發起的native調用的指令是什么
      //3 取出參數,拿到JS傳過來的數據
      //4 根據指令調用對應的native方法,傳遞數據
      return true;
    }
    return super.shouldOverrideUrlLoading(view, url);
  }
  2)UIWebView ​webView:shouldStartLoadWithRequest:navigationType:
    (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
      //1 根據url,判斷是否是所需要的攔截的調用 判斷協議/域名
      if (是){
        //2 取出路徑,確認要發起的native調用的指令是什么
        //3 取出參數,拿到JS傳過來的數據
        //4 根據指令調用對應的native方法,傳遞數據
        return NO;
        //確認攔截,拒絕WebView繼續發起請求
      }
      return YES;
    }
  3)WKWebView webView:decidePolicyForNavigationAction:decisionHandler:
    (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
      //1 根據url,判斷是否是所需要的攔截的調用 判斷協議/域名
      if (是){
        //2 取出路徑,確認要發起的native調用的指令是什么
        //3 取出參數,拿到JS傳過來的數據
        //4 根據指令調用對應的native方法,傳遞數據
 
        //確認攔截,拒絕WebView繼續發起請求
        decisionHandler(WKNavigationActionPolicyCancel);
      }else{
        decisionHandler(WKNavigationActionPolicyAllow);
      }
      return YES;
    }
 
 

  3.Native調用 Js

  • 使用evaluatingJavaScript 執行JS代碼:
相比於 JavaScript 調用 Native, Native 調用 JavaScript 較為簡單,畢竟不管是 iOS 的 UIWebView 還是 WKWebView,還是 Android 的 WebView 組件,都以子組件的形式存在於 View/Activity 中,Native想要調用JS的時候,可以把數據與調用的JS函數,通過字符串拼接成JS代碼,交給WebView進行執行,直接調用相應的 API 即可執行拼接 JavaScript 字符串。所以設計成為暴露一個如JSBridge的對象供native調用。
 
  • 舉個例子
1)JS中存在函數jsfunction()
function jsfunction(data){
  console.log(JSON.parse(data))
  //1 識別客戶端傳來的數據
  //2 對數據進行分析,從而調用或執行其他邏輯
}
2)客戶端用OC拼接字符串,拼出js代碼,並用json傳遞數據
//data是一個字典,把字典序列化
NSString *paramsString = [self _serializeMessageData:data];
NSString* javascriptCommand = [NSString stringWithFormat:@"jsfunction('%@');", paramsString];
//要求必須在主線程執行JS
if ([[NSThread currentThread] isMainThread]) {
  [self.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
} else {
  __strong typeof(self)strongSelf = self;
  dispatch_sync(dispatch_get_main_queue(), ^{
    [strongSelf.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
  });
}
這段代碼只是用來拼接出這個字符串:jsfunction('{data:xxx,data2:xxx}');
再用evaluatingJavaScript或loadUrl對js代碼進行執行,即完成了native對js方法的調用
 

  4.通信過程整理

  • S1. 客戶端內H5里發送請求scheme,aweme://profile?douyin_id=233;
  • S2. 客戶端內webview容器攔截所有請求,挨個請求做字符串匹配,判斷是否以 aweme:// 開頭;
  • S3. scheme命中匹配,客戶端拆解出后面的參數得到操作名和對應的操作參數;
  • S4. 客戶端執行對應的操作,打開個人profile頁;
  • S5. Native功能調用完畢。
 

  5.回調設計

  • 場景問題:例如分享成功后增加積分等場景,h5網頁如何知道客戶端執行完畢,並執行相應回調呢?
  • 參照JSONP信息傳遞的執行過程:
JSONP利用<script>標簽沒有跨域限制的特點來達到與第三方通訊的目的
調用方需要提供一個回調函數 比如cb20180725 來接收數據:
  • 第三方包裝callback參數作為函數名來包裹住響應的JSON數據如cb20180725({"param": “1111”})
  • 1. 在window上生成掛載一個隨機名的全局函數,函數里執行success的回調函數;
  • 2.創建script標簽,載入請求地址;
  • 3.服務端返回一段執行window上cb20180725函數的js代碼: cb20180725({"param": “1111”})
  • 4.瀏覽器中執行完成jsonp回調。
 
  • JSBridge回調過程:
 
  • 1,H5發起scheme請求之前,隨機生成一個callback_id,掛到window上;
  • 2,在callback_id指向的唯一函數里,執行想要的回調函數
  • 3,H5發送請求scheme,bytedance://profile?douyin_id=233&callback_id=dy20180724;
  • 4,客戶端內webview容器攔截所有請求,挨個請求做字符串匹配,匹配是否以 bytedance:// 開頭;
  • 5, scheme命中匹配,客戶端拆解出后面的參數得到操作名和對應的操作參數;
  • 6,客戶端執行對應的操作,打開個人profile頁;
  • 7,執行完對應操作之后,客戶端調用webview接口執行回調 javascript:dy20180724(json_data)
關鍵點:生成回調函數callback_id,回調函數存儲(掛載在window)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM