Search code examples
androidandroid-webviewgoogle-glass

Android WebView is empty if rendered from SurfaceHandler


I am writing an app for Google Glass (SDK level 15) and need to show WebView to display some html in it. To test it I defined a layout with 2 controls - TextView and WebView.

All rendering I made in SurfaceHolder.Callback. Important is that callback methods are executed on a background thread.

All worked fine in debug mode - TextView and WebView showed content fine. But as soon as I switched to Run config app crashed with exception:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.(Handler.java:121) at android.webkit.ZoomControlExternal.(ZoomControlExternal.java:36) at android.webkit.ZoomManager.getCurrentZoomControl(ZoomManager.java:1188) at android.webkit.ZoomManager.dismissZoomPicker(ZoomManager.java:1204) at android.webkit.ZoomManager.onSizeChanged(ZoomManager.java:946) at android.webkit.WebView.onSizeChanged(WebView.java:5855) at android.view.View.setFrame(View.java:11362) at android.webkit.WebView.setFrame(WebView.java:5829) at android.view.View.layout(View.java:11273) at android.view.ViewGroup.layout(ViewGroup.java:4224) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486) at android.widget.LinearLayout.onLayout(LinearLayout.java:1399) at android.view.View.layout(View.java:11279) at android.view.ViewGroup.layout(ViewGroup.java:4224) at android.widget.FrameLayout.onLayout(FrameLayout.java:431) at android.view.View.layout(View.java:11279) at android.view.ViewGroup.layout(ViewGroup.java:4224) at android.widget.FrameLayout.onLayout(FrameLayout.java:431) at android.view.View.layout(View.java:11279) at android.view.ViewGroup.layout(ViewGroup.java:4224) at com.adnecs.fit.TestDrawer.surfaceChanged(TestDrawer.java:61) at com.google.android.glass.timeline.RemoteSurfaceHolder.setParameters(RemoteSurfaceHolder.java:104) at com.google.android.glass.timeline.LiveCard$1.performDirectRenderingOperation(LiveCard.java:190) at com.google.android.glass.timeline.ITimelinePublisher$Stub.onTransact(ITimelinePublisher.java:60) at android.os.Binder.execTransact(Binder.java:338) at dalvik.system.NativeStart.run(Native Method)

The reason is clear - layout method needed to be called on UI thread. I passed a Handler object from UI thread and tried to run 'layout' method on it. Worked well for TextView but WebView is always blank.

Below is the changed code (I left old version commented):

@Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {

        final int measuredWidth = View.MeasureSpec.makeMeasureSpec(i2, View.MeasureSpec.EXACTLY);
        final int measuredHeight = View.MeasureSpec.makeMeasureSpec(i3, View.MeasureSpec.EXACTLY);
//      mView.measure(measuredWidth, measuredHeight);
//      mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
//      draw();

        mView.getHandler().post(
                new Runnable() {
                    @Override
                    public void run() {
                        mView.measure(measuredWidth, measuredHeight);
                        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
                        draw();
                    }
                }
        )    ;
    }

So my questions are:

  1. Why did it work in Debug mode and failed to run in Run mode?
  2. Why isn't WebView showing anything if I change code as above?

Appreciate your help!


Solution

  • Was able to figure this out (also would be nice if more knowledgable people could comment or correct me)

    First of all, you must run WebView updates on UI thread.

    1. Why it worked in Debug: looks like it is all about timing. When you run loadData on WebView it takes some time to process it. If you call draw immediately after you set the data - control will still be empty - content page is not yet ready.

    2. What I did was to run rendering thread until page was loaded. To determine if page was loaded I used webview.setWebViewClient to register callback and react on onPageFinished.