hybrid入门及JsBridge原理

什么是hybrid


hybrid:混合使用Native和web技术开发,即前端和客户端的混合开发。核心是快速迭代,无需审核。
主流技术框架

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

webview:是app的一个组件,用于加载内嵌的h5页面,即一个小型的浏览器内核
file协议:加载本地资源文件(http(s)协议则是网络请求,加载网络资源)

具体实现:
前端的静态页面交给客户端,以文件形式存储在app中,客户端在webview中使用file协议加载静态资源文件。

使用NA:体验要求极致,变化不频繁(新闻资讯类app的首页)
使用hybrid:体验要求高,变化频繁(新闻资讯类app的新闻详情页)
使用h5:体验无要求,不常用(如活动、反馈、举报页面)

hybrid和h5的比较

开发运维成本高,适用于产品的稳定功能,体验要求高,迭代频繁(产品型);
h5适用于单次的活动页面或不常用页面(运营型);

hybrid更新上线流程

上传静态资源包到服务端,客户端每次启动检查包文件版本是否更新,下载新的包文件并解压修改成新的版本号。
(服务端的版本和zip包维护,更新zip包之前,先对比版本号。)

js和客户端通讯:JSBridge


JSBridge是实现web端和native端双向通讯的一种机制,以javascript引擎或者webview容器为媒介,通过约定协议进行通讯。
而Schema协议是前端和客户端通讯的约定(形式就是常见的url)。

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 Schema

兼容性好,但不直观、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内容

// 1.拦截URLSchema
// 2.注入JS api 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一样,十分容易理解。

你可能感兴趣的:(hybrid-app)