23

I am developing an open-source browser called Lightning Browser, and I have run into some trouble with the most recent update to Android (4.2.2).

WebViews will fail to render completely until the view is touched. It only occurs on the latest Android version. On 4.2.1, the WebView rendered completely fine. I use my Nexus 7 for development and right after I received the 4.2.2 update, the browser stopped rendering. Other users experienced this as well, and it was confirmed multiple times to happen ONLY on 4.2.2. It is happening targeting API level 16 and 17, but I have seen a WebKit browser that targets API level 9 without this problem.

I have attempted to remedy this using the solution to a problem I found here on Stack Overflow (Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy). HOWEVER, setting the WebView's RenderPriority to high alone does not solve it... the only way to get it to render without being touched was to place the invalidate() command inside the OnDraw() method of WebView. This causes the WebView to re-draw continuously. This works (sort of), animations are smooth, the page loads very quickly, but it causes the WebView's performance to drop otherwise.

I have also seen this question (very similar to mine), but there is no good answer for it. Android WebView Fails to Completely Render Content Until User Interaction

By performance drop, I mean input. Text input lags, and the WebView itself cannot handle what is going on as well as before. Benchmarking the browser with the invalidate() method call drops Benchmarking performance by around 8%. I know benchmarks aren't everything, but it's telling me that the continuous drawing is straining the system and causing the system to ignore other tasks.

In Conclusion... The WebView in Android 4.2.2 will not render until touched. The only way I know to fix this is to call invalidate() in the onDraw() method of WebView. This is bad for performance and I am looking for a different way to fix this.

The Magic Code (I'm currently using)...

class MyWebView extends WebView
{
    @Override
    onDraw(Canvas canvas)
    {
        invalidate(); 
        super.OnDraw(canvas);
    }
}

remove

invalidate();

and it doesn't render until touched.

Does anyone have a suggestions on how make the WebView render (other than what I've done)? By the way, this is my first question I've asked here on Stack, so forgive me if I haven't been clear or if I've done something wrong.

EDIT: I found this question here about a similar issue and it was resolved, the problem is, I don't understand what the answer even means. If anyone could enlighten me it might help.

I get this error in logcat

E/chromium(1243): external/chromium/net/disk_cache/backend_impl.cc:2022: [0705/172030:ERROR:backend_impl.cc(2022)] Corrupt Index file
Community
  • 1
  • 1
anthonycr
  • 4,146
  • 1
  • 28
  • 35
  • That problem is not related to our issue, the page is blank, but because there are exotic problems loading it, nothing to do with the rendering pipeline. – rupps May 09 '13 at 23:28
  • BTW... Also in my case, the Keyboard/input performance is so terrible (and decreases as the page complexity increases) that I opted by, with JQuery at runtime, trigger a native input overlay on selecting a HTML input. It's not very elegant, but the native inputs work flawlessly, and Text Selection / cut / paste is not frustrating ... – rupps May 09 '13 at 23:46
  • An alternative that does not lower performance http://stackoverflow.com/a/19253633/236743 :) – Dori Oct 08 '13 at 16:49

4 Answers4

9

So I finally found the issue causing the WebView not to render. I am starting and stopping the animation of a drawable inside the WebViewClient of my WebView. This drawable is simply a refresh button that rotates when the page is loading... simple right?

Well in the non-fullscreen mode of the browser, the rotating drawable and the WebView are both children of the same parent, whereas in the fullscreen mode, the drawable becomes a child of the WebView. Somehow, by starting the animation when the page is loading and stopping it when it's done (in fullscreen), the application decides that the rotating drawable is what needs all the drawing power and the WebView never draws. When in fullscreen mode and the drawable becomes a child of the WebView, the WebView then has a higher rendering priority than the drawable and it draws fine.

The moral of the story is... WebViews like to be the highest priority view to be drawn. If there are other views that get greater priority, they won't draw correctly.

I'm no expert on animations, so I need to rethink how I'm animating the drawable now so as to not interrupt the WebView.

Hopefully that made sense. Thanks for reading.

anthonycr
  • 4,146
  • 1
  • 28
  • 35
  • 1
    Speaking about animations, I've seen that if you apply a dummy HTML animation to somewhere in the page, it will trigger a webview redraw ... but it's a very dirty hack .... – rupps May 09 '13 at 23:23
  • OMG. That's exactly what I was doing! And I was stuck since many days why the drawables don't load. Thanks a lot. So the answer is not using the spinning animation? – Shobhit Puri Apr 16 '14 at 06:11
  • But this does not happen on a device running Android Kitkat. Did they fix something? – Shobhit Puri Apr 16 '14 at 15:06
4

Disable hardware acceleration on your manifest:

android:hardwareAccelerated="false"

SerX
  • 61
  • 1
2

please see the last answer by @Olivier on Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy , he triggers a couple delayed invalidates on WebView's OnTouchEvent rather than continuosly on onDraw ... In my case it worked because (most) of my problems were after a user touching webview and I changing some CSS in response. Of course it doesn't apply at all on the case of automatic / timeout'ed css

In my case it also helped to export a JavaScript function from Java to manually trigger invalidate with a delay, so if in JavaScript you more or less know where the disaster might happen you can manually trigger it, something like this inner class inside your WebView:

public class MyWebView extends WebView {

    private class InvalidateExtension {

        private Handler handler=new Handler(); // you might already have a handler
        private Runnable mInvalidater=new Runnable() {

            @Override
            public void run() {
                MyWebView.this.invalidate();
            }

        }

        public void trigger(int delay) {
                handler.postDelayed(mInvalidater, delay);
                handler.postDelayed(mInvalidater, 2*delay); // just in case
                handler.postDelayed(mInvalidater, 4*delay); // just in case just in case :)
        }
    }


    /** Call this function on this view's init, BEFORE loading a page so it is available to JS */
    private void call_me_on_init_to_enable_hack() {
        addJavascriptInterface(new InvalidateExtension(), "Invalidater");
    }
}

So, from JavaScript you can do:

Invalidater.trigger(100);

And play with the milliseconds value ....

Hope this helps someone !

Community
  • 1
  • 1
rupps
  • 9,712
  • 4
  • 55
  • 95
2

There is a problem with the zoom scale when rotating the phone and that will cause this problem, parts or the whole page will not be drawn. To solve this override onScaleChanged in your WebViewClient implementation and invalidate the view. That should force a redraw when needed.

@Override
    public void onScaleChanged(WebView view, float oldScale, float newScale) {
        if (view != null) {
            view.invalidate();
        }
    }