[TIMOB-19977] evalJS() fails for large WebViews on Android M
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | n/a |
Status | Open |
Resolution | Unresolved |
Affected Version/s | Release 5.0.2 |
Fix Version/s | n/a |
Components | Android |
Labels | Community, android-6, evaljs, timeout, webview |
Reporter | KT |
Assignee | Unknown |
Created | 2015-10-18T20:20:05.000+0000 |
Updated | 2018-02-28T19:55:56.000+0000 |
Description
JS is being used within a webview to modify and/or return information about the contents. Above a certain yet-to-be determined size, it is now timing out (as of Android M). At first thought the issue might be Titanium 5.0; but reverting to the previous 3.5.1 (as I had skipped 4.x due to a host of other issues) didn't fix the problem.
For instance, I'm loading jQuery on the pages with the webview, and calling it externally (i.e., from the Titanium JS via evalJS()). One use is to set the body font. On most HTML files, it works. On a larger one (again, above some threshold), it times out with the warning from getJSValue() in TiWebViewBinding.java: "Timeout waiting to evaluate JS". The same is the case for *any* evalJS() on the webviews holding larger HTML content.
Now, I don't know if this is a Titanium problem or an Android problem, so at this point I'm trying to gather enough information about how exactly Titanium does its evalJS() call — and how exactly it times out — to triage what's going on. So, for instance, in getJSValue(), by the time that try {} block is hit and we're waiting for returnSemaphore.tryAcquire(3500, TimeUnit.MILLISECONDS)) to time out, presumably the JS call has already been executed (and, in this case, failed for whatever reason)?
Where exactly does the JS call get dispatched to the webview? In AppBinding? I assume the (relative) complexity of this is due to the async nature of the webview vs. evalJS() waiting for a return value (hence the semaphore and timeout)?
I'm faintly hopeful that increasing the timeout, or allowing the option to do so, might alleviate the problem — but not very. Because the effect of the failed JS call — such as the aforementioned jQuery call to change the body font — is never seen.
(Sorry, throwing jQuery in there as an example may have confused the issue. The first JS call in my app that times out doesn't involve jQuery at all; many of them don't.)
As a further data point, it's not always the same evalJS() calls that fail. But even if I log all evalJS() calls, it appears that the timeout message is async and therefore — because the timeout message contains no information about the content of the timed-out call — then, unless I'm wrong, it's impossible to tell which one it was that timed out.
Okay, so I've pretty much figured out how evalJS() is handled on both sides of the bridge. And yeah, while I realize simulating a synchronous JS call in Android isn't necessarily pretty, one thing I'm confused about: why is so much of it being done on the JS side of things? You're blocking on the Java side, anyway, so why not dispatch directly with a loadUrl() or (now) evaluateJavascript() — which has the benefit of a callback, even — instead of queuing up eval() calls on the JS side and polling[!] there? In looking into this, a lot of people have been dealing with the same set of circumstances, namely mimicking iOS's synchronous JS call, and while many use the same type of wrapper, no one is doing that sort of JS-side dispatching and polling. And probably not just because that introduces a whole other potential vector for failure, and I'm suspicious that may be what's happening in this case.
Hello [~kt], Please provide a testcase about your issue, so we can regenerate the problem. Thanks.
If I have time I will. But to be honest I have some other things on my plate, and I'm probably just going to try to move everything to native Android to save future headaches, and as a faster solution. Thanks.