uknownothingsnow / JsBridge

android java and javascript bridge, inspired by wechat webview jsbridge
9.78k stars 2.01k forks source link

存在多个致命bug! #119

Open wendux opened 6 years ago

wendux commented 6 years ago

在使用的过程中,起初遇到了一些bug, 是不敢相信的,毕竟4000多星的项目,近1000 fork,我所有项目加起来也没这么多啊。但是随着使用中遇到的越来越多的问题,我不得不仔细的看了看源码,结合现有的issue, 经过仔细反复的测试、验证,让我大为吃惊,发现此库不仅有一些不合理的地方,而且存在多个致命的问题。在此,先说严重的bug, 文末贴出了我自己实现的修复版,欢迎探讨:

致命的bug

  1. 字符串转义bug。如issue #103 、#53、 #83、 #42、 #35、 #24、 #3、#6 ... 这些问题本质上的原因都是通过js bridge传递数据转义有误导致。 此bug会导致严重问题,如果传递的数据转义发生错误时,将导致不可用,像WebViewJavascriptBridge: WARNING: javascript handler threw.", source: (1) 这种错误很多时候都是因为js收到的数据和期望不符导致的异常(当然有些也有可能是js hanlder 处理不当抛出的)。这是一个偶现的致命bug 。要彻底解决这个问题最根本的方法就是不应该去转义,因为在传递数据格式未限定的情况下,只要转义,正常的数据字符串中都有可能匹配到转义规则(而这些字符串本身是不需要转义),这将会导致对于一部分数据能够正常转义,而一部分数据不能,这样的bug很难测试。 如果非要转义,就必须得限定jsbridge数据传递的格式,比如必须以json形式传递(不能直接传递string、bool等基础类型),这样才可以应用固定的转义规则解析。

  2. Javascript调用原生方法会偶现失败。相同的问题如issue #96、#76、#111 在测试过程中发现,失败的时机往往是webview调用 onPageFinished 前后,具体的表现是js调用native方法时 shouldOverrideUrlLoading(包括两种重载)没有被触发,所以端上没有去刷新js调用的message queue. 至于为什么没有就调用shouldOverrideUrlLoading,这是因为js和webview通信机制有问题,通过改变iframe src属性的这种方式并不能保证shouldOverrideUrlLoading每次都会被调用,这也是一些其它android jsbridge 会出现此问题的原因。解决的办法很多,此处不赘述。

  3. Java调用JS方法会偶现失败。相同的问题如issue #116、#89、#71 等,在经过分析之后,导致此问题的原因有两个(除过作者所述的 “maven 的方式添加这个库的话有问题,应该是js没有打包进去”):

一些其它方面的拙见

  1. 使用不够简单,源密码中不仅提供了 BridgeWebView.java, 而且也提供了 BridgeWebViewClient.java, 如果使用者要提供自定义的 WebViewClient就必须得继承 BridgeWebViewClient.java, 为什么不在BridgeWebView中做一个代理呢?我看到有人因为没有继承BridgeWebViewClient.java而导致调用js handler不成功的issue 如 #61、 #23 。

  2. js 发送消息给java 为什么不直接发送? 而是通知java 有消息 , 然后再来取消息呢?这样的开销是很没必要的。

  3. 对于前端开发者来说,一份代码往往需要在Android、ios两个平台下都能运行。作者应该提供或给出ios下的实现或实现规范。

我的修复版

另,在研究的过程中,我重新写了一个WebViewJavascriptBridge ,它不仅与IOS版本marcuswestin/WebViewJavascriptBridge 完全兼容,并且修复了上述的bug, 在此我贴出地址,欢迎大家来讨论: https://github.com/wendux/WebViewJavascriptBridge

ghost commented 6 years ago

干的漂亮!/竖大拇指

shenshuo commented 6 years ago

华为的android 不支持怎么办

Jayin commented 6 years ago

不错!

wuxiaojun123 commented 6 years ago

您好!Javascript调用原生方法会偶现失败,这个有哪些解决办法呢?

AndreTsao commented 5 years ago

luotext commented 5 years ago

在使用的过程中,起初遇到了一些bug, 是不敢相信的,毕竟4000多星的项目,近1000 fork,我所有项目加起来也没这么多啊。但是随着使用中遇到的越来越多的问题,我不得不仔细的看了看源码,结合现有的issue, 经过仔细反复的测试、验证,让我大为吃惊,发现此库不仅有一些不合理的地方,而且存在多个致命的问题。在此,先说严重的bug, 文末贴出了我自己实现的修复版,欢迎探讨:

致命的bug

1. 字符串转义bug。如issue #103 、#53、 #83、 #42、 #35、 #24、 #3、#6 ...  这些问题本质上的原因都是通过js bridge传递数据转义有误导致。 此bug会导致严重问题,如果传递的数据转义发生错误时,将导致不可用,像`WebViewJavascriptBridge: WARNING: javascript handler threw.", source: (1)` 这种错误很多时候都是因为js收到的数据和期望不符导致的异常(当然有些也有可能是js hanlder 处理不当抛出的)。这是一个**偶现**的致命bug 。要彻底解决这个问题最根本的方法就是不应该去转义,因为在传递数据格式未限定的情况下,只要转义,正常的数据字符串中都有可能匹配到转义规则(而这些字符串本身是不需要转义),这将会导致对于一部分数据能够正常转义,而一部分数据不能,这样的bug很难测试。 如果非要转义,就必须得限定jsbridge数据传递的格式,比如必须以json形式传递(不能直接传递string、bool等基础类型),这样才可以应用固定的转义规则解析。

2. Javascript调用原生方法会偶现失败。相同的问题如issue #96、#76、#111 在测试过程中发现,失败的时机往往是webview调用 onPageFinished 前后,具体的表现是js调用native方法时 shouldOverrideUrlLoading(包括两种重载)没有被触发,所以端上没有去刷新js调用的message queue. 至于为什么没有就调用shouldOverrideUrlLoading,这是因为js和webview通信机制有问题,**通过改变iframe src属性的这种方式并不能保证shouldOverrideUrlLoading每次都会被调用**,这也是一些其它android jsbridge 会出现此问题的原因。解决的办法很多,此处不赘述。

3. Java调用JS方法会偶现失败。相同的问题如issue #116、#89、#71 等,在经过分析之后,导致此问题的原因有两个(除过作者所述的 “maven 的方式添加这个库的话有问题,应该是js没有打包进去”):

* 正如上面(1)中所述,是字符串转义的bug导致,这种情况下,js handler在收到java传递过来的数据时,仍然按照期望的数据格式处理,倘若在java传递数据之前的转义发生了错误,那么hanlder很可能会抛出异常,此时就会输出 `WebViewJavascriptBridge: WARNING: javascript handler threw."...`这种错误

* Java的调用根本没有dispatch 到js中去,我们看一下这一块源码:
 void dispatchMessage(Message m) {
        ...
        // 必须要找主线程才会将数据传递出去 --- 划重点
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            this.loadUrl(javascriptCommand);
        }
    }

我们可以看到,只有当是主线程时,才会执行分发的js,那么如果不是主线程呢?那自然就不会被分发,如果要确保这段代码没问题,那么就必须保证dispatchMessage是在主线程中被调用,而callHandler最终会调用dispatchMessage,所以就得保证callHandler必须在主线程调用。如果强制让用户去遵守这个规则是不靠谱的,况且有些时候,用户也不知道自己是否在主线程,示例代码中有在 onPageFinished时调用callHandler,不错onPageFinished在大多数情况下都会在主线程中被回调,但是我查了一圈,从没看到任何官方文档有说onPageFinished会在主线程中被回调。所以,那些出现的callHanlder不能调用bug的魅族手机,最好先去检查一下是否在主线程调用的。 所以,库中是应该保证callHandler无论是在哪个线程发起的调用, 最终的js都能在主线程被执行(因为webview要执行 s代码只能在主线程).

一些其它方面的拙见

1. 使用不够简单,源密码中不仅提供了 `BridgeWebView.java`, 而且也提供了 `BridgeWebViewClient.java`, 如果使用者要提供自定义的 `WebViewClient`就必须得继承 `BridgeWebViewClient.java`, 为什么不在`BridgeWebView`中做一个代理呢?我看到有人因为没有继承`BridgeWebViewClient.java`而导致调用js handler不成功的issue 如 #61、 #23 。

2. js 发送消息给java 为什么不直接发送? 而是通知java 有消息 , 然后再来取消息呢?这样的开销是很没必要的。

3. 对于前端开发者来说,一份代码往往需要在Android、ios两个平台下都能运行。作者应该提供或给出ios下的实现或实现规范。

我的修复版

另,在研究的过程中,我重新写了一个WebViewJavascriptBridge ,它不仅与IOS版本marcuswestin/WebViewJavascriptBridge 完全兼容,并且修复了上述的bug, 在此我贴出地址,欢迎大家来讨论: https://github.com/wendux/WebViewJavascriptBridge

我就是一直抛这个问题,还是偶现的!头大。

VliceZ commented 4 years ago

使用过程中踩到的一些坑和解决办法写在简书了,希望可以帮助到有需要的人 。 https://www.jianshu.com/p/20c250624d94