Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

存在多个致命bug! #119

Open
wendux opened this issue Jan 23, 2018 · 7 comments
Open

存在多个致命bug! #119

wendux opened this issue Jan 23, 2018 · 7 comments

Comments

@wendux
Copy link

wendux commented Jan 23, 2018

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

致命的bug

  1. 字符串转义bug。如issue WebViewJavascriptBridge: WARNING: javascript handler threw #103java方法调用不到js方法 #53使用js.接收从android返回的数据。报如下错误。 #83Android 向 JS层传递数据 现在被改成只能传中文了 #42IF Js data contains "%" will be throw illegal token in URLDecoder.decode(url, "UTF-8"); #35如果native经过jsBridge传递给js的数据中有”%5C“,会报illegal token #24BridgeUtil.SPLIT_MARK使用 “/”是个糟糕的决定 #3Send data is a JSON string to js while happen error  #6 ... 这些问题本质上的原因都是通过js bridge传递数据转义有误导致。 此bug会导致严重问题,如果传递的数据转义发生错误时,将导致不可用,像WebViewJavascriptBridge: WARNING: javascript handler threw.", source: (1) 这种错误很多时候都是因为js收到的数据和期望不符导致的异常(当然有些也有可能是js hanlder 处理不当抛出的)。这是一个偶现的致命bug 。要彻底解决这个问题最根本的方法就是不应该去转义,因为在传递数据格式未限定的情况下,只要转义,正常的数据字符串中都有可能匹配到转义规则(而这些字符串本身是不需要转义),这将会导致对于一部分数据能够正常转义,而一部分数据不能,这样的bug很难测试。 如果非要转义,就必须得限定jsbridge数据传递的格式,比如必须以json形式传递(不能直接传递string、bool等基础类型),这样才可以应用固定的转义规则解析。

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

  3. Java调用JS方法会偶现失败。相同的问题如issue callHandler无法调用js的方法,registerHandler可以响应 #116魅族手机 webview.registerHandler调不起来 #89BridgeWebViewClient 调用callHandler有25%导致桥接失败 #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 如 BridgeWebView一旦设置了setWebViewClient后就监听不到js方法了,求解决 #61why can not I use setWebViewClient? #23

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

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

我的修复版

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

@ghost
Copy link

ghost commented Jan 23, 2018

干的漂亮!/竖大拇指

@shenshuo
Copy link

shenshuo commented Feb 6, 2018

华为的android 不支持怎么办

@Jayin
Copy link

Jayin commented Feb 7, 2018

不错!

@wuxiaojun123
Copy link

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

@JetCyC
Copy link

JetCyC commented Dec 26, 2018

@luotext
Copy link

luotext commented Jan 26, 2019

在使用的过程中,起初遇到了一些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
Copy link

VliceZ commented Dec 19, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants