在Android WebView中获取HTTP状态码

37

我正在开发一个Android应用,其中一个WebView会加载一个网站,但有时候该网站会返回HTTP代码500。

我的问题是:是否有办法通过监听器或其他类从WebView中获取HTTP状态码?

我尝试了实现一个WebViewClient,但无法获取WebView接收到的HTTP状态码。


你能在SDK < 23上获取状态码吗? - rraallvv
9个回答

18

目前(我写这篇文章的时候)似乎不可能实现。对于onReceiveError()的文档至多也只是模糊的解释,但如果你看一下这个问题:

http://code.google.com/p/android/issues/detail?id=968

很明显,HTTP状态码不能通过这种机制来报告。如何可能开发人员编写WebView却没有任何方法来检索HTTP状态码,这真的让我感到难以置信。


5
从SDK 23开始,情况已不再如此。 - Martin Epsz
并非每个人都想/能购买最先进的手机。我的许多客户仍然使用Android 4或5,因此这种情况在未来几年仍将存在。 - soger
使用onReceivedHttpError(...) - 请参见下面的答案 - Jaroslav

14

您可以使用ajax加载内容,然后将其放入webview中使用loadDataWithBaseURL。

创建一个像这样的网页

<script type="text/javascript">
    function loadWithAjax() {
        var httpRequest = new XMLHttpRequest();
        var path = 'PATH';
        var host = 'HOST';
        var url = 'URL';

        httpRequest.onreadystatechange = function(){
            if (httpRequest.readyState === 4) { 
                if (httpRequest.status === 200) {
                    browserObject.onAjaxSuccess(host, url, httpRequest.responseText);
                } else {
                    browserObject.onAjaxError(host, url, httpRequest.status);
                }
            }
        };

        httpRequest.open('GET', path, true);
        httpRequest.send(null);
    }
</script>
<body onload="loadWithAjax()">

browserObject 是注入到 JavaScript 中的 Java 对象。

addJavascriptInterface(this, "browserObject");

将其加载到webView中。您应该使用您的值替换路径/网址。

ajaxHtml = IOUtils.toString(getContext().getAssets().open("web/ajax.html"));
            ajaxHtml = ajaxHtml.replace("PATH", path);
            ajaxHtml = ajaxHtml.replace("URL", url);
            ajaxHtml = ajaxHtml.replace("HOST", host);

loadDataWithBaseURL(host, ajaxHtmlFinal, "text/html", null, null);

然后像这样处理onAjaxSuccess/onAjaxError:

    public void onAjaxSuccess(final String host, final String url, final String html)
    {
        ((Activity) getContext()).runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                loadDataWithBaseURL(url, html, "text/html", null, null);
            }
        });
    }

    public void onAjaxError(final String host, final String url, final int errorCode)
    {

    }

现在你可以处理http错误。


1
你的建议很好,当应用程序开发人员也可以修改相关网站时,这看起来非常棒。但是,如果应用程序开发人员无法控制网站的HTML / Javascript呢?那么你会把loadWithAjax()放在哪里呢?谢谢。 - Regex Rookie
你好。不错但有点长。 - HF_

13
你可以重写WebViewClientonReceivedHttpError方法,在那里你可以看到状态码:
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
    super.onReceivedHttpError(view, request, errorResponse);
    int statusCode = errorResponse.getStatusCode();
    // TODO: do your logic..
}

5

看起来可以通过 Android M API 中的新回调函数实现此功能(https://code.google.com/p/android/issues/detail?id=82069#c7)。

void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)

不幸的是,这很可能不适用于 Android M 之前的设备。


3

实际上,检查HTTP状态以查看内容是否可加载到Web视图中并不太难。假定:您已经设置了Web视图,并且具有目标URL的字符串,则...

    AsyncTask<Void, Void, Void> checkURL = new AsyncTask<Void, Void, Void>() {
        @Override
        protected void onPreExecute() {
            pd = new ProgressDialog(WebActivity.this, R.style.DickeysPDTheme);
            pd.setTitle("");
            pd.setMessage("Loading...");
            pd.setCancelable(false);
            pd.setIndeterminate(true);

            pd.show();
        }
        @Override
        protected Void doInBackground(Void... arg0) {
            // TODO Auto-generated method stub
            int iHTTPStatus;

            // Making HTTP request
            try {
                // defaultHttpClient
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpGet httpRequest = new HttpGet(sTargetURL);

                HttpResponse httpResponse = httpClient.execute(httpRequest);
                iHTTPStatus = httpResponse.getStatusLine().getStatusCode();
                if( iHTTPStatus != 200) {
                    // Serve a local page instead...
                    wv.loadUrl("file:///android_asset/index.html");
                }
                else {

                    wv.loadUrl(sTargetURL);     // Status = 200 so we can loard our desired URL
                }

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                // Show a toast for now...
                Toast.makeText(WebActivity.this, "UNSUPPORTED ENCODING EXCEPTION", Toast.LENGTH_LONG).show();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
                // Show a toast for now...
                Toast.makeText(WebActivity.this, "CLIENT PROTOCOL EXCEPTION", Toast.LENGTH_LONG).show();

            } catch (IOException e) {
                e.printStackTrace();
                // Show a toast for now...
                Toast.makeText(WebActivity.this, "I/O EXCEPTION", Toast.LENGTH_LONG).show();

            }  catch (Exception e) {
                e.printStackTrace();
                // Show a toast for now...
                Toast.makeText(WebActivity.this, "GENERIC EXCEPTION", Toast.LENGTH_LONG).show();

            }

            return null;
        }


    };

    checkURL.execute((Void[])null);

在其他地方,在WebViewClient中,关闭进度对话框。
@Override  
    public void onPageFinished(WebView view, String url)  
    {
        // Do necessary things onPageFinished
        if (pd!=null) {
            pd.dismiss();
        }

    }       

4
在那种情况下,我们不是要加载两次页面吗? - Jose Gómez

2

目前您无法获得正常的HTTP响应代码。

但是,如果您可以修改Web应用程序服务器端,请使用以下方法:

在服务器上创建一些JavaScript函数,例如riseHttpError

在Android端使用JavaScript接口,当您需要告诉Android处理HTTP错误时,只需在服务器上调用Android.riseHttpError()

Android将处理此函数,并且您将能够在Android端执行所需操作。

在我的解决方案中需要获取错误。您可以发送任何想要的代码。 :)

当然,这只是另一种变化方式。因此,可能存在其他更好的解决方案。

但是,如果您可以修改服务器端,我认为最好使用URLHandler进行双重请求。


1

我认为从webView中以简单的方式(如果有可能的话)获取状态码是不可能的。

我的想法是使用WebViewClient的onReceivedError()方法(正如你所说),并在WebViewClient中定义错误(完整的错误列表可在此处找到:http://developer.android.com/reference/android/webkit/WebViewClient.html),并假设例如504状态码等于WebViewClient.ERROR_TIMEOUT等。


谢谢,但是它不起作用,我实现了那个方法,但是webview没有触发它。我会尝试另一种方式,如果成功了,我会在这里为大家发布解决方案。 - Victor Escobar
你是否在WebView对象上调用了setWebViewClient()方法?请粘贴你的代码。 - Mike
`public class EnlaceWebClient extends WebViewClient { public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){ Log.d("ERROR","Error code:"+Integer.toString(errorCode)); }}` - Victor Escobar

0

实现简单Activity的完整示例

适用于API Level >= 23

Java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView myWebView = (WebView)findViewById(R.id.webview);
        myWebView.loadUrl("https://www.fñdghd.es");
        myWebView.setWebViewClient(new CustomWebViewClient());
    }

    private class CustomWebViewClient extends WebViewClient
    {
        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
        {
            super.onReceivedError(view, request, error);
            Log.e("MainActivity",(String) error.getDescription());
            Log.e("MainActivity",String.valueOf(error.getErrorCode()));

            //Todo: Something to define.
        }
    }
}

C# 适用于 Xamarin 或 Maui

public class WebViewActivity : Activity
{
    protected override void OnCreate(Bundle? savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.activity_webview);

        WebView myWebView = (WebView)FindViewById(Resource.Id.webview);
        myWebView.LoadUrl("https://www.google.es");
        myWebView.SetWebViewClient(new CustomWebViewClient());
    }
}

internal class CustomWebViewClient : WebViewClient
{
    public override void OnReceivedError(WebView view, IWebResourceRequest request, WebResourceError error)
    {
        base.OnReceivedError(view, request, error);
        Console.WriteLine(error.Description);
        Console.WriteLine(error.ErrorCode);

        //Todo: Something to define.
    }
}

-1

在页面完成后,您应该使用此功能。

@Override 
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error){
    //Your code to do
    Toast.makeText(
        getActivity(), 
        "Your Internet Connection May not be active Or " + error,
        Toast.LENGTH_LONG
    ).show();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接