简体   繁体   中英

WebViewClient onPageFinished never called

I've looked at several similar posts on S/O but I can't find an answer to my problem. I'm trying to grab the html from a web view but I can't get my "onPageFinished() callback to fire. I've even tried using the progress callback on the WebChromeClient but nothing is working. neigh the web client or web chrome client receive any callbacks. I am running this code as an Android unit test and using a test HTML from the assets directory which I've confirmed via assert and debug is being located. What am I missing?

** Update ** I've stepped through the test method in debug. It does load the html (it's a unit test so I cannot actually see the rendered content). It then blocks on the wait() call, times out then performs the assert at the bottom without ever receiving a callback from either client. (Both the web client and chrome client should/would call notify())

The difference here would be that I am running within an Android unit test. The WebView is NOT attached to any Activity or container. I'm wondering if that has anything to do with the missing callbacks. I'll try sticking it on an Activity to see if it makes any difference. ** / Update **

public class MyWebViewTest extends InstrumentationTestCase {

    private WebView webView;
    private String html;

    public void setUp() throws Exception {
        super.setUp();
        Context context = getInstrumentation().getContext();
        this.webView = new WebView(context);
        assertTrue("requires test.html",
                Arrays.asList(context.getResources().getAssets().list("www")).contains("test.html"));
    }

    public void tearDown() throws Exception {

    }

    @JavascriptInterface
    public void viewContent(String content) {
        this.html = content;
    }
    public void testCanLoadAndReadWebContentFromWebView() throws Exception {
        webView.addJavascriptInterface(this, "_webContainer");
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView webView, String url) {
                super.onPageFinished(webView, url);
                onPageLoaded(webView);
            }

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
            }
        });

        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                if(newProgress==100)
                    onPageLoaded(webView);
            }
        });

        webView.loadUrl("file:///android_asset/www/test.html");
        synchronized (this) {
            wait(5000);
        }
        assertNotNull(html);
    }


    private void onPageLoaded(WebView webView) {
        webView.loadUrl("javascript:window._webContainer.viewContent(document.documentElement.innerHTML);");
        synchronized (this) {
            notify();
        }
    }
}

I was correct in my assumption! When the WebView is not attached to an Activity it will not load, render, or do much of anything. I ended up extending ActivityInstrumentationTestCase2 and attaching the web view to an Activity. At that point my breakpoints in the callbacks started lighting up. Here's my working test case:

public class MyWebViewTest extends ActivityInstrumentationTestCase2<TestMeActivity> {

    private WebView webView;
    private String html;

    public MyWebViewTest() {
        super(TestMeActivity.class);
    }

    public void setUp() throws Exception {
        super.setUp();
        Context context = getInstrumentation().getContext();
        this.webView = getActivity().getView();
        assertTrue("requires test.html",
                Arrays.asList(context.getResources().getAssets().list("www")).contains("test.html"));
    }

    public void tearDown() throws Exception {

    }

    @JavascriptInterface
    public void viewContent(String content) {
        this.html = content;
    }

    public void testCanLoadAndReadWebContentFromWebView() throws Exception {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                configureWebView();
            }
        });
        synchronized (MyWebViewTest.this) {
            MyWebViewTest.this.wait(25000);
        }
        assertEquals("<head></head><body>\n" +
                "Hello\n" +
                "\n" +
                "</body>",html);
    }

    private void configureWebView() {
        webView.addJavascriptInterface(this, "_webContainer");
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView webView, String url) {
                super.onPageFinished(webView, url);
                onPageLoaded(webView);
            }
        });

        webView.loadUrl("file:///android_asset/www/test.html");
    }


    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void onPageLoaded(WebView webView) {
        webView.evaluateJavascript("javascript:window._webContainer.viewContent(document.documentElement.innerHTML);",
                new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        synchronized (MyWebViewTest.this) {
                            MyWebViewTest.this.notify();
                        }
                    }
                }
        );
    }
}

and here's the Activity I threw together to back it:

public class TestMeActivity extends Activity{

    private WebView view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        view = new WebView(this);
        setContentView(view);
    }

    public WebView getView() {
        return view;
    }
}

In short there is quite a bit of work involved in testing the contents of a WebView from an Android TestCase. You have to add a Javascript interface to the web view, insert some Javascript to invoke the internal Javascript interface object, passing the contents of the web view, through the interface and finally collect it in your test case. I sure wish it were easier but this is the best I can come up with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM