什么是移動端通信?
有哪些是需要了解的開發背景知識?
怎么樣開始一個移動端項目開發和調試呢?
看這篇文檔記錄,夠入門 go--->
1 IOS 和Android底層和js的交互原理
ios提供UIWebView 組件,是一個可加載網頁的對象,提供類似瀏覽器的功能,可以通過js代碼調用一些原生的功能,比如:獲取GPS信息。
Safari瀏覽器的控件和原生 UIWebView不兼容,在ios8版本之后提供了WKWebView對象,提供了4個屬性、8個api供js調用:
loading:是否處於加載中,
canGoBack:只讀,是否可以接受后退
canGoForward: 只讀,是否可接受向前
request: url 請求
loadData: 設置主頁類型,MIME類型,文檔編碼,base url 。
loadRequest: 加載網絡資源
loadHTMLString: 加載本地 html 資源
stopLoading: 停止加載
goBack: 后退
goForward: 前進
reload: 重新加載
stringByEvaluatingJavaScriptFromString: 執行一段 js 腳本,並返回執行結果。
2 IOS 系統
Native 調用 JavaScript的方法 (object-c swift)
是獲取 stringByEvaluatingJavaScriptFromString 方法執行的放回結果。
// Swift webview.stringByEvaluatingJavaScriptFromString("Math.random()") // OC [webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];
以上例子的函數都是相當於在 window 下的方法,所以如果js的方法放在 window 下是可以被 webview的接口調用的。
類似:
"Math.random()"
JS 調用 Native 方法
native沒有現成的api給js調用,可以通過發送網絡請求的方式,在 native 層得到通知。
UIWebView 層內發起網絡請求,格式是這樣的,例如 object-c 和 swift
jsbridge://methodName?param1=value1¶m2=value2 // js 調用原生的一般傳參格式。
所以,在UIWebView層如果看到這樣的schema就進行邏輯處理,不進行網絡請求。
發起這種網絡請求的方式,h5可以用 location.href 和 iframe 的方式。
location.href 會有點問題 連續發送多個請求時,在 Native 層只能接收到最后一次的調用。
所以使用 iframe發送請求。
var url = 'jsbridge://doAction?title=標題'; // 定義請求的url,設置好對終端有效的schema。方便攔截。 var iframe = document.createElement('iframe'); iframe.style.width = '1px'; iframe.style.height = '1px'; iframe.style.display = 'none'; iframe.src = url; document.body.appendChild(iframe); // 將iframe添加到document,發起請求 setTimeout(function() { iframe.remove(); // 只需要一次請求,所以需要把 iframe 標簽從HTML文檔中移除。 }, 100);
WebView 可以攔截這個請求並獲取對應的參數,參考如下代碼
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { print("shouldStartLoadWithRequest") // 開始處理這個請求 let url = request.URL let scheme = url?.scheme let method = url?.host // 獲取host值,判斷執行的動作,交給 Object-c 處理。 let query = url?.query // 獲取執行的傳參 if url != nil && scheme == "jsbridge" { print("scheme == \(scheme)") print("method == \(method)") print("query == \(query)") switch method! { case "getData": self.getData() case "putData": self.putData() case "gotoWebview": self.gotoWebview() case "gotoNative": self.gotoNative() case "doAction": self.doAction() case "configNative": self.configNative() default: print("default") } return false; } else { return true; } }
3 Android 系統
js 調用 native 的方式
在 Android 系統中有 2 種方式可以實現,js調用 native方法。好像還有第3中,重寫console.log方法不明覺厲中。。
方法一:
通過schema方式,使用shouldOverrideUrlLoading
方法對url協議進行解析。這種js的調用方式與ios的一樣,使用iframe來調用native代碼。
方法二:
使用原生webview提供的接口,addJavaScriptInterface 方法來實現。
class JSInterface { @JavascriptInterface //注意這個代碼一定要加上,Java 代碼對類型方法的修飾。 public String getUserData() { return "---- native 定義的方法"; } }
webView.addJavascriptInterface(new JSInterface(), "AndroidJS");復制代碼上面的代碼就是在頁面的window對象里注入了AndroidJS對象。在js里可以直接調用
console.log(AndroidJS.getUserData()) // ---- native 定義的方法 JSInterface 接口的方法。 對應在 Android WebView 方法中定義的被修飾后的方法。
native 調用 js 的方式
webView.loadUrl("javascript:Bridge.doSomething('hello.')");
調用 JavaScript 中的 Bridge.doSomething() 方法。
4 庫的封裝
js 調用 native 的封裝
基於之前對 IOS 和Android 和js通信的了解,再封裝一層,保證js代碼操作系統兼容性。 https://juejin.im/post/599a58f6f265da247b4e756b 這段代碼來自掘金的文章。
我再整理下邏輯並注釋。^_^
HaveJsBridge = { doCall: function(functionName, data, callback) { var _this = this; // 這里其實是一個節流,不讓你點太快或響應太快 if (this.lastCallTime && (Date.now() - this.lastCallTime) < 100) { setTimeout(function() { _this.doCall(functionName, data, callback); }, 100); return; } this.lastCallTime = Date.now(); data = data || {}; if (callback) { $.extend(data, { callback: callback }); } if (UA.isIOS()) { $.each(data, function(key, value) { // 每個 data key value 進行序列化 if ($.isPlainObject(value) || $.isArray(value)) { data[key] = JSON.stringify(value); } }); var url = Args.addParameter('jsbridge://' + functionName, data); var iframe = document.createElement('iframe'); iframe.style.width = '1px'; iframe.style.height = '1px'; iframe.style.display = 'none'; iframe.src = url; document.body.appendChild(iframe); setTimeout(function() { iframe.remove(); // 和上面的代碼執行一樣,清除dom }, 100); } else if (UA.isAndroid()) {
// 此處對接上面👆代碼中的 webView.addJavascriptInterface 接口方法 window.androidJS && window.androidJS[functionName] && window.androidJS[functionName](JSON.stringify(data)); } else { console.error('未獲取platform信息,調取api失敗'); } } }
好,兼容性准備工作就緒,現在開始封裝幾個 常用的 js可以調用 native 的方法。
!!這幾個方法都對應上面的 func webView 函數 👆!!
1 getData(datatype, callback, extra) H5從Native APP獲取數據
JSBridge.getData('userInfo',function(data) { console.log(data); });
2 putData(datatype, data) H5告訴Native APP一些數據
JSBridge.putData('userInfo', { username: 'zhangsan', age: 20 });
3 gotoWebview(url, page, data) 打開相應網頁傳參,打開新的webview 窗口,可以再 webView函數中調用
JSBridge.gotoWebview('http://www.youzan.com', 'goodsDetail', { goods_id: 10000, title: '這是商品的標題', desc: '這是商品的描述' });
4 打開某個原生 Native APP 的頁面
JSBridge.gotoNative('loginPage', { username: '張三' });
5 做一些操作
// 封裝一個復制函數,比如點擊復制 JSBridge.doAction('copy', { content: '這是要復制的內容' }); // 封裝一個分享函數,比如點擊分享到 哪哪哪。 JSBridge.doAction('share', { title: '分享標題', desc: '分享描述', link: 'http://www.youzan.com', imgs_url: 'http://wap.koudaitong.com/v2/common/url/create?type=homepage&index%2Findex=&kdt_id=63077&alias=63077' }); // 注意,這些代碼還只是方法的偽代碼實現不要當真。
5 Safari 上的調試
第一步: 首先需要打開Safari的調試模式,在Safari的菜單中,選擇“Safari”→“Preference”→“Advanced”,勾選上“Show Develop menu in menu bar”選項
第二步: 打開iPhone模擬器的調試模式,在真機或iPhone模擬器中打開設置界面,選擇“Safari”→“高級”→“Web檢查器”,選擇開啟
第三步: 將真機通過USB連上電腦,或者開啟模擬器,Safari的“Develop”菜單下便會多出相應的菜單項
第四步: Safari連接上UIWebView之后,我們就可以直接在Safari中直接修改HTML、CSS,以及調試Javascript
什么是 UIWebView 啟動呢?
這個是Android開發需要Java環境,起一個Android的項目,在手機或模擬器上安裝這個項目,就跑起來了。
6 參考文檔
UIWebView https://developer.apple.com/documentation/uikit/uiwebview
WKWebView
Github JSBridge https://github.com/marcuswestin/WebViewJavascriptBridge
搭建簡單webview調試 https://www.jianshu.com/p/4f783cd34ab1