hybrid入門及JsBridge原理

什麼是hybrid


hybrid:混合使用Native和web技術開發,即前端和客戶端的混合開發。核心是快速迭代,無需審覈。
主流技術框架javascript

  • web渲染:Cordova(前身是PhoneGap)
  • 原生渲染:React Native、 Weex
  • 混合渲染:微信小程序

webview:是app的一個組件,用於加載內嵌的h5頁面,即一個小型的瀏覽器內核
file協議:加載本地資源文件(http(s)協議則是網絡請求,加載網絡資源)前端

具體實現:
前端的靜態頁面交給客戶端,以文件形式存儲在app中,客戶端在webview中使用file協議加載靜態資源文件。
QQ截圖20191013102246.png
使用NA:體驗要求極致,變化不頻繁(新聞資訊類app的首頁)
使用hybrid:體驗要求高,變化頻繁(新聞資訊類app的新聞詳情頁)
使用h5:體驗無要求,不經常使用(如活動、反饋、舉報頁面)java

hybrid和h5的比較

開發運維成本高,適用於產品的穩定功能,體驗要求高,迭代頻繁(產品型);
h5適用於單次的活動頁面或不經常使用頁面(運營型);git

hybrid更新上線流程

上傳靜態資源包到服務端,客戶端每次啓動檢查包文件版本是否更新,下載新的包文件並解壓修改爲新的版本號。
(服務端的版本和zip包維護,更新zip包以前,先對比版本號。)github

js和客戶端通信:JSBridge

QQ截圖20191010125333.png
JSBridge是實現web端和native端雙向通信的一種機制,以javascript引擎或者webview容器爲媒介,經過約定協議進行通信。
而Schema協議是前端和客戶端通信的約定(形式就是常見的url)。
QQ截圖20191017154424.pngweb

window['_invoke_scan_callback_'] = function(result){
    alert(result)
}
var iframe = document.createElement('iframe');
iframe.style.display = none;
iframe.src = 'weixin://dl/scan?k1=v1&k2=v2&callback=_invoke_scan_callback_'; 
var body = document.body || document.getElementsByTagsName('body')[0];
body.appendChild('iframe');
setTimeout(function(){
    body.removeChild(iframe);
    iframe = null;
})


window.invoke.share({title: 'xxx', content: 'xxx'}, function(result){
    if(result.errno === 0){
        alert('分享成功~~~~')
    } else {
        // 分享失敗
        alert(result.message)
    }
})

(function (window, undefined) {

    // 調用 schema 的封裝
    function _invoke(action, data, callback) {
        // 拼裝 schema 協議
        var schema = 'myapp://utils/' + action

        // 拼接參數
        schema += '?a=a'
        var key
        for (key in data) {
            if (data.hasOwnProperty(key)) {
                schema += '&' + key + data[key]
            }
        }

        // 處理 callback
        var callbackName = ''
        if (typeof callback === 'string') {
            callbackName = callback
        } else {
            callbackName = action + Date.now()
            window[callbackName] = callback
        }
        schema += 'callback=callbackName'

        // 觸發
        var iframe = document.createElement('iframe')
        iframe.style.display = 'none'
        iframe.src = schema  // 重要!
        var body = document.body
        body.appendChild(iframe)
        setTimeout(function () {
            body.removeChild(iframe)
            iframe = null
        })
    }

    // 暴露到全局變量
    window.invoke = {
        share: function (data, callback) {
            _invoke('share', data, callback)
        }
        // ...
    }

})(window)
// invoke.js內置到客戶端,客戶端每次啓動webview都默認執行。實現本地加載

1.攔截webView請求的URL Schemaajax

兼容性好,但不直觀、url長度有限制
開源實現:JsBridge
通信的基本形式:調用能力,傳遞參數,監聽回調

2.向webView注入JS api小程序

簡單直觀,Android 4.2+版本 
開源實現:DsBridge
function showNativeDialog(showBtn, text){
    if(showBtn === 'showBtn1'){
        // 通常是建立iframe元素節點,經過設定src屬性發起請求
        window.alert(`jsBridge://showNativeDialog?text=${text}攔截URLSchema`);
    }else {
        // 向webView注入JS api方法通信
        window.NativeBridge.showNativeDialog(`${text}    向webView注入JS api`);
    }
}

客戶端獲取內容,而後JS通信拿到內容,再渲染。微信小程序

JSbridge實現Demo

1.web端點擊按鈕,彈出Native端彈出框,顯示web端input內容api

<div class="ipt">
    <input type="text" id="editText" placeholder="請輸入內容">
</div>
// 1.攔截URLSchema
<button id="showBtn1">show Native Dialog(攔截URLSchema)</button>
<hr>
// 2.注入JS api
<button id="showBtn2">show Native Dialog(向webView注入JS api)</button>


function showNativeDialog(showBtn, text){
    if(showBtn === 'showBtn1'){
        // 通常是建立iframe元素節點,經過設定src屬性發起請求
        window.alert(`jsBridge://showNativeDialog?text=${text}攔截URLSchema`);
    }else {
        // 調用原生端的彈窗方法
        window.NativeBridge.showNativeDialog(`${text}    向webView注入JS api`);
    }
}

private WebView webView;
private MainActivity self = this;
    
webView.setWebChromeClient(new WebChromeClient(){
    // 攔截webView請求的URL Schema實現jsBridge,重寫對象攔截彈窗(開源實現JsBridge)
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        if (!message.startsWith("jsBridge://")){
            return super.onJsAlert(view, url, message, result);
        }
        // 符合自定義url Schema,執行native彈窗方法
        String text = message.substring(message.indexOf("=") + 1);
        self.showNativeDialog(text);

        result.confirm();
        return true;
    }
});

// 顯示原生彈窗
private void showNativeDialog (String text) {
    new AlertDialog.Builder(this).setMessage(text).create().show();
}

// 向webView注入JS api(開源實現DsBridge)
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
class NativeBridge {
    private Context ctx;
    NativeBridge(Context ctx){
        this.ctx = ctx;
    }
    @JavascriptInterface
    public void showNativeDialog (String text) {
        new AlertDialog.Builder(ctx).setMessage(text).create().show();
    }
}

2.Native端點擊按鈕,彈出web端彈出框,顯示Native端input內容

showBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 拿到native輸入的內容,並調用顯示web彈窗的方法
       String iptValue = editText.getText().toString();
       self.showWebDialog(iptValue);
    }
});

//  顯示web彈窗
private void showWebDialog (String text) {
    String jsCode = String.format("window.showWebDialog('%s')", text);
    webView.evaluateJavascript(jsCode, null);
}


window.showWebDialog = text => alert(text);

dsbridge開源庫實現一個小demo

1.web端發送原生http請求
2.原生端實現換膚功能

// 1. native端定義JsApi類,實現nativeRequest方法。用來接收web發送過來的url字符串,獲取地址而後發送原生http請求。

public class JsApi{
        @JavascriptInterface
        public void nativeRequest(Object params, CompletionHandler handler)  {
            try {
                String url = ((JSONObject)params).getString("url");
                String data = request(url);
                handler.complete(data);
            } catch (Exception e) {
                handler.complete(e.getMessage());
                e.printStackTrace();
            }
        }

        private String request(String urlSpec) throws Exception {
            HttpURLConnection connection = (HttpURLConnection) new URL(urlSpec).openConnection();
            connection.setRequestMethod("GET");
            InputStream inputStream = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuffer result = new StringBuffer();
            String line;
            while ( (line = reader.readLine()) != null){
                result.append(line);
            }
            // 將result轉爲字符串(http請求返回的內容)
            return result.toString();
        }
    }
    
// web端發送url信息,並返回獲取的請求內容。
sendBtn.addEventListener("click", e => {
        dsBridge.call("nativeRequest", { url: urlText.value}, data => {
            response.textContent = data;
        })
    });
    
2.定義changeTheme方法,改變狀態欄、標題欄、導航欄、web頁面的背景顏色。而後使用原生的菜單欄,調用changeTheme方法。
private void changeTheme (int color){
    // 狀態欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    getWindow().setStatusBarColor(color);

    // 標題欄
    getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color));

    // 導航欄
    getWindow().setNavigationBarColor(color);

    // web網頁
    dwebView.callHandler("changeTheme", new Object[]{color});
}

//web端改變背景色
dsBridge.register("changeTheme", color => {
    // andriod 0xFFFF0000 ARGB
    // web RGB/RGBA
    document.body.style.backgroundColor = '#' + (color & 0x00FFFFFF).toString(16);
})

demo上傳地址: github地址https://github.com/NidusP/hybrid-demo

簡單瞭解一些hybrid開發的知識,JSbridge做爲web端與native端的溝通橋樑,其中schema協議和http(s)與file協議同樣,規定了一種通信方式,這種基本形式(調用能力,傳遞參數,監聽回調)與ajax同樣,十分容易理解。

相關文章
相關標籤/搜索