JSBridge

JSBridge概念

JSBridge顾名思义就是是一座用JavaScript搭建起来的桥,一端是web,一端是native。可以实现web 与Native之间相互调用。


实现原理

Native>>Web

  • Android

    webview.loadUrl(“JavaScript:function()”);

  • IOS

    webview.stringByEvaluatingJavaScriptFromString("JavaScript:function()");


Web>>Native

Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现

IOS

UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&m2=value2,于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    print("shouldStartLoadWithRequest")
    let url = request.URL
    let scheme = url?.scheme
    let method = url?.host
    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()
            default:
                print("default")
        }

        return false
    } else {
        return true
    }
}

Android

在Android开发中,能实现Js调用Java,有4种方法:

  1. JavascriptInterface
  2. WebViewClient.shouldOverrideUrlLoading();
  3. WebChromeClient.onConsoleMessage();
  4. WebChromeClient.onJsPrompt()
JavascriptInterface

这是Android提供的Js与Native通信的官方解决方案。
首先Java代码要实现这么一个类,它的作用是提供给Js调用。

public class JavascriptInterface {

  @JavascriptInterface
  public void showToast(String toast) {
    Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
  }
}

然后把这个类添加到WebView的JavascriptInterface中。webView.addJavascriptInterface(new JavascriptInterface(), “javascriptInterface”); 在Js代码中就能直接通过“javascriptInterface”直接调用了该Native的类的方法

function showToast(toast) {
   javascript:javascriptInterface.showToast(toast);
}

但是这个官方提供的解决方案在Android4.2之前存在严重的安全漏洞(乌云)。在Android4.2之后,加入了@JavascriptInterface才得到解决。所以考虑到兼容低版本的系统,JavascriptInterface并不适合。

WebChromeClient.onConsoleMessage()####

这是Android提供给Js调试在Native代码里面打印日志信息的API,同时这也成了其中一种Js与Native代码通信的方法。在Js代码中调用console.log(‘xxx’)方法。

console.log('log message that is going to native code')

就会在Native代码的WebChromeClient.consoleMessage()中得到回调。consoleMessage.message()获得的正是Js代码console.log(‘xxx’)的内容。

public class CustomWebChromeClient extends WebChromeClient {

@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
    super.onConsoleMessage(consoleMessage);
    String msg = consoleMessage.message();//JavaScript输入的Log内容
  }
}

WebChromeClient.onJsPrompt()####

其实除了WebChromeClient.onJsPrompt(),还有WebChromeClient.onJsAlert()和WebChromeClient.onJsConfirm()。顾名思义,这三个Js给Native代码的回调接口的作用分别是展示提示信息,展示警告信息和展示确认信息。鉴于,alert和confirm在Js的使用率很高,所以JSBridge的解决方案中都倾向于选用onJsPrompt()。
Js中调用

 window.prompt(message, value)

WebChromeClient.onJsPrompt()就会受到回调。onJsPrompt()方法的message参数的值正是Js的方法window.prompt()的message的值。

public class CustomWebChromeClient extends WebChromeClient {

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)        {
    // 处理JS 的调用逻辑
    result.confirm();
    return true;
}

WebViewClient.shouldOverrideUrlLoading()####

这个方法的作用是拦截所有WebView的Url跳转。页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了

public class CustomWebViewClient extends WebViewClient {

  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (isJsBridgeUrl(url)) {
        // JSbridge的处理逻辑
        return true;
    }
    return super.shouldOverrideUrlLoading(view, url);
  }
}  

前端核心调用

var url = 'jsbridge://doAction?title=分享标题&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';  
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();
  }, 100);

通过修改window.location.href也可以达到发起网络请求的效果,但是有一个很严重的问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。所以JS端发起网络请求的时候,需要使用iframe,这样就可以避免这个问题

实现方案

Android:

https://github.com/lzyzsd/JsBridge

https://github.com/papastar/JsBridge(Vanke)

IOS

https://github.com/marcuswestin/WebViewJavascriptBridge

样例代码

 //界面跳转
    webView.registerHandler("route", new BridgeHandler() {
        @Override
        public void handler(String data, CallBackFunction function) {
            WebViewResult result = new WebViewResult();
            try {
                //Router.getInstance().openUrl(data);
                Uri uri = Uri.parse(data);
                if(TextUtils.equals(uri.getScheme(),"zze")){
                    String lastPath = uri.getLastPathSegment();
                }
                result.setCode(WebViewResult.CODE_SUCCESS);
            }catch (Exception e){
                result.setCode(WebViewResult.CODE_FAIL);
            }finally {
                function.onCallBack(result.getResult());
            }
            //Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
            //function.onCallBack("submitFromWeb exe, response data 中文 from Java");
        }

    });
    //分享
    webView.registerHandler("share", new BridgeHandler() {
        @Override
        public void handler(String data, CallBackFunction function) {
            WebViewResult result = new WebViewResult();
            try {
                String url = JSONUtils.getString(data,"title","");
                String desc = JSONUtils.getString(data,"desc","");
                String link = JSONUtils.getString(data,"link","");
                String imgurl = JSONUtils.getString(data,"imgurl","");
                doShare(url,desc,link,imgurl);
                result.setCode(WebViewResult.CODE_SUCCESS);
            }catch (Exception e){
                result.setCode(WebViewResult.CODE_FAIL);
            }finally {
                function.onCallBack(result.getResult());
            }
        }

    });
    //获取数据
    webView.registerHandler("getdata", new BridgeHandler() {
        @Override
        public void handler(String data, CallBackFunction function) {
            WebViewResult<User> result = new WebViewResult<>();
            try {
                String type = JSONUtils.getString(data,"type","");
                if(TextUtils.equals("1",type)){
                    User user = new User();
                    user.name = "Papa";
                    user.phone = "134XXXXXXXX";
                    user.token = "755fgw4twtr1g5gewq";
                    user.userId = "5678995";
                    result.setData(user);
                }
                result.setCode(WebViewResult.CODE_SUCCESS);
            }catch (Exception e){
                result.setCode(WebViewResult.CODE_FAIL);
            }finally {
                function.onCallBack(result.getResult());
            }
        }

    });

前端调用

      //call native method
        window.WebViewJavascriptBridge.callHandler(
            'share'
            , {'title': '分享标题','desc': '分享内容','link': '分享链接','imgurl': '分享图片链接'}
            , function(responseData) {
                document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
            }
        );

参考文档

https://juejin.im/entry/573534f82e958a0069b27646

http://blog.csdn.net/sbsujjbcy/article/details/50752595

http://tech.youzan.com/jsbridge/

http://www.jianshu.com/p/9fd80b785de1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,976评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,249评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,449评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,433评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,460评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,132评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,721评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,641评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,180评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,267评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,408评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,076评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,767评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,255评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,386评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,764评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,413评论 2 358

推荐阅读更多精彩内容

  • JSBridge 1. Why do we need JSBridge? 2. Why is “JS”Bridge...
    loveqin阅读 9,180评论 0 7
  • 在Android中,JSBridge已经不是什么新鲜的事物了,各家的实现方式也略有差异。大多数人都知道WebVie...
    Jannonx阅读 1,332评论 0 5
  • 为了提高客户端的开发效率以及手机对h5技术的支持好不够完美,hybird技术被推上了历史舞台。它的基本原理是使用前...
    狐尼克朱迪阅读 3,759评论 0 10
  • JSBrige系列直通车,由浅入深理解JS-Native的通信过程:JSbridge系列解析(一):JS-Nati...
    zizi192阅读 5,638评论 0 14
  • 一阵风过,熄灭了眼睛里的星星,倾倒了眼睛里的海。 一阵风过,梦里的白衣少年不在,夕阳稻草,空余一片暮霭。 那时心动...
    大昵昵阅读 157评论 0 0