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:
Appreciate your help!
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.
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.
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
.