JSBridge——Web与Native交互之iOS篇

作者: 王干 | 来源:发表于2015-09-17 23:17 被阅读47436次

    一、原理篇

    在讲解原理之前,首先需要了解下iOS提供的UIWebView组件,苹果官方是这么介绍UIWebView组件的:

    You can use the UIWebView class to embed web content in your application. To do so, you simply create a UIWebView object, attach it to a window, and send it a request to load web content. You can also use this class to move back and forward in the history of webpages, and you can even set some web content properties programmatically.

    UIWebView是iOS内置的浏览器控件,UIWebView用于在APP中嵌入网页,通常是html网页,也可以是PDF、txt文档等。但需要注意的是,Safari浏览器使用的浏览器控件和UIwebView组件并不是同一个,两者在性能上有很大的差距。幸运的是,苹果发布iOS8的时候,新增了一个WKWebView组件,如果你的APP只考虑支持iOS8及以上版本,那么你就可以使用这个新的浏览器控件了。

    关于UIWebView类,你需要知道的一些属性和方法

    属性:

    • loading:是否处于加载中
    • canGoBack:A Boolean value indicating whether the receiver can move backward. (只读)
    • canGoForward:A Boolean value indicating whether the receiver can move forward. (只读)
    • request:The URL request identifying the location of the content to load. (read-only)

    方法:

    • loadData:Sets the main page contents, MIME type, content encoding, and base URL.
    • loadRequest:加载网络内容
    • loadHTMLString:加载本地HTML文件
    • stopLoading:停止加载
    • goBack:后退
    • goForward:前进
    • reload:重新加载
    • stringByEvaluatingJavaScriptFromString:执行一段js脚本,并且返回执行结果

    1.1 Native(Objective-C或Swift)调用Javascript方法

    Native调用Javascript语言,是通过UIWebView组件的stringByEvaluatingJavaScriptFromString方法来实现的,该方法返回js脚本的执行结果。

    // Swift
    webview.stringByEvaluatingJavaScriptFromString("Math.random()")
    // OC
    [webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];
    

    1.2 Javascript调用Native(Objective-C或Swift)方法

    反过来,Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现。UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&param2=value2

    于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑。

    首先通过iframe发起一个网络请求,这个请求的作用是唤起Native APP的分享组件,将网页分享到朋友圈或分享给其他好友。JS端的核心代码如下所示:

    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);
    

    然后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
            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
            }
        }
    

    二、库的封装

    在项目的实践中,我们逐渐抽象出一些通用的方法,这些方法基本上都是可以满足项目的需求。如下所示:

    1.getData(datatype, callback, extra) H5从Native APP获取数据

    使用场景:H5需要从Native APP获取某些数据的时候,可以调用这个方法。

    参数 类型 是否必须 示例值 说明
    datatype String userInfo 数据类型
    callback Function 回调函数
    extra Object 传递给Native APP的数据对象

    示例代码:

    JSBridge.getData('userInfo',function(data) {
        console.log(data);
    });
    

    2.putData(datatype, data) H5告诉Native APP一些数据

    使用场景:H5告诉Native APP一些数据,可以调用这个方法。

    参数 类型 是否必须 示例值 说明
    datatype String userInfo 数据类型
    data Object { username: 'zhangsan', age: 20 } 传递给Native APP的数据对象

    示例代码:

    JSBridge.putData('userInfo', {
        username: 'zhangsan',
        age: 20
    });
    

    3.gotoWebview(url, page, data) Native APP新开一个Webview窗口,并打开相应网页

    参数 类型 是否必须 示例值 说明
    url String http://www.youzan.com 网页链接地址,一般都只要传递URL参数就可以了
    page String web 网页page类型,默认为web
    data Object 额外参数对象

    示例代码:

    // 示例1:打开一个网页
    JSBridge.gotoWebview('http://www.youzan.com');
    
    // 示例2:打开一个网页,并且传递额外的参数给Native APP
    JSBridge.gotoWebview('http://www.youzan.com', 'goodsDetail', {
        goods_id: 10000,
        title: '这是商品的标题',
        desc: '这是商品的描述'
    });
    

    4.gotoNative(page, data) 从H5页面跳转到Native APP的某个原生界面

    参数 类型 是否必须 示例值 说明
    page String loginPage Native页面标示符,例如loginPage
    data Object { username: 'zhangsan', age: 20 } 额外参数对象

    示例代码:

    // 示例1:打开Native APP登录页面
    JSBridge.gotoNative('loginPage');
    
    // 示例2:打开Native APP登录页面,并且传递用户名给Native APP
    JSBridge.gotoNative('loginPage', {
        username: '张三'
    });
    

    5.doAction(action, data) 功能上的一些操作

    参数 类型 是否必须 示例值 说明
    action String copy 操作功能类型,例如分享、复制
    data Object { content: '这是要复制的内容' } 额外参数

    示例代码:

    // 示例1:调用Native APP复制一段文本到剪切板
    JSBridge.doAction('copy', {
        content: '这是要复制的内容'
    });
    
    // 示例2:调用Native APP的分享组件,分享当前网页到微信
    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'
    });
    

    三、调试篇

    2.1 使用Safari进行UIWebView的调试

    (1)首先需要打开Safari的调试模式,在Safari的菜单中,选择“Safari”→“Preference”→“Advanced”,勾选上“Show Develop menu in menu bar”选项,如下图所示。


    2-1

    (2)打开真机或iPhone模拟器的调试模式,在真机或iPhone模拟器中打开设置界面,选择“Safari”→“高级”→“Web检查器”,选择开启即可,如下图所示。


    2-2
    (3)将真机通过USB连上电脑,或者开启模拟器,Safari的“Develop”菜单下便会多出相应的菜单项,如图所示。 Paste_Image.png

    (4)Safari连接上UIWebView之后,我们就可以直接在Safari中直接修改HTML、CSS,以及调试Javascript。

    Paste_Image.png

    四、一些坑

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

    五、参考链接

    相关文章

      网友评论

      • __MAX:#define __wvjb_js_func__(x) #x

        能帮给解释一下这个宏的原理吗
      • darr250:如果js需要webview返回数据,比如调用分享的成功的状态,这个状态怎么返回给js呢?
      • d4a41519402c:发现jsbridge在IOS10好像有问题,使用不了呢
      • 0a78031db704:LZ如果JS端有大数据你怎么带过来的,不会也是通过拼接在url的方式吧?
        这样感觉没真正的成为JSbridge,封装性和扩展性还是弱了一些。
        另外,你的回调,是每次webView调用JS代码么,这样每个接口就被孤立的割裂成两步了
        096854250229:@blesser 实际上如果JS架构得足够好的话,是不会割裂的,比如我写的这套https://github.com/yanglang1987500/AppInterface,由发布订阅系统组织的一个完整会话。至于大数据,呵呵,一般有大数据都会去服务器端查,不会让js传递给客户端的。
        y8aEu4:@blesser 我觉着要真是有大数据的话,就不太适合用这种模式调用了。而封装性我觉得这篇文章讲的主要是基础原理,所以需要自己去封装,或者使用现成封装好的。

      本文标题:JSBridge——Web与Native交互之iOS篇

      本文链接:https://www.haomeiwen.com/subject/pmubcttx.html