【问题标题】:HttpURLConnection timeout settingsHttpURLConnection 超时设置
【发布时间】:2011-02-17 12:01:32
【问题描述】:

如果 URL 连接时间超过 5 秒,我想返回 false - 这怎么可能使用 Java?这是我用来检查 URL 是否有效的代码

HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);

【问题讨论】:

    标签: java url timeout


    【解决方案1】:

    HttpURLConnection 有一个setConnectTimeout 方法。

    只需将超时设置为5000毫秒,然后捕捉java.net.SocketTimeoutException

    您的代码应如下所示:

    try { HttpURLConnection.setFollowRedirects(false); HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("HEAD"); con.setConnectTimeout(5000); //set timeout to 5 seconds return (con.getResponseCode() == HttpURLConnection.HTTP_OK); } catch (java.net.SocketTimeoutException e) { return false; } catch (java.io.IOException e) { return false; }

    【讨论】:

    • 我将值设置为 10 分钟。然而,它甚至在 2 分钟结束之前给我一个java.net.ConnectException: Connection timed out: connect。你知道是什么导致了这个问题吗?
    • SocketTimeoutException 是 IOException 的子类。如果两个 catch 块做同样的事情,你可以只捕获 IOException。
    • @spaarky21 是正确的。但是,如果您正在构建 UI 并且想要通知用户发生超时,则必须在 IOException 之前捕获 SocketTimeoutException,否则将无法访问。
    • 注意!!! 您需要在任何隐式连接的方法之前调用setConnectTimeout(基本上是所有已连接时抛出 IllegalStateException 的方法)。理想情况下,首先调用 setConnectTimeout (readTimeout) 方法。
    • 它对我不起作用。但是,添加con.setReadTimeout() 后,它按预期工作。
    【解决方案2】:

    你可以这样设置超时,

    con.setConnectTimeout(connectTimeout);
    con.setReadTimeout(socketTimeout);
    

    【讨论】:

    • 我们可以指定的最大超时时间是多少?
    • @Pacerier 文档没有明确说明这一点。如果值为负数(值为 0 表示无限期等待),它会抛出 IllegalArgumentException。由于超时是一个无符号的 32 位整数,我猜最大超时大约是 49 天(尽管我严重怀疑这个值对任何人都有帮助)。
    • 我同时使用了超时,但仍然出现超时错误。但是当我检查日志时,我的请求需要 20 秒,但我给了 100 秒的 setConnectTimeout
    【解决方案3】:

    如果 HTTP 连接没有超时,您可以在后台线程本身(AsyncTask、Service 等)中实现超时检查器,以下类是自定义 AsyncTask 的示例,在一定时间后超时

    public abstract class AsyncTaskWithTimer<Params, Progress, Result> extends
        AsyncTask<Params, Progress, Result> {
    
    private static final int HTTP_REQUEST_TIMEOUT = 30000;
    
    @Override
    protected Result doInBackground(Params... params) {
        createTimeoutListener();
        return doInBackgroundImpl(params);
    }
    
    private void createTimeoutListener() {
        Thread timeout = new Thread() {
            public void run() {
                Looper.prepare();
    
                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
    
                        if (AsyncTaskWithTimer.this != null
                                && AsyncTaskWithTimer.this.getStatus() != Status.FINISHED)
                            AsyncTaskWithTimer.this.cancel(true);
                        handler.removeCallbacks(this);
                        Looper.myLooper().quit();
                    }
                }, HTTP_REQUEST_TIMEOUT);
    
                Looper.loop();
            }
        };
        timeout.start();
    }
    
    abstract protected Result doInBackgroundImpl(Params... params);
    }
    

    一个示例

    public class AsyncTaskWithTimerSample extends AsyncTaskWithTimer<Void, Void, Void> {
    
        @Override
        protected void onCancelled(Void void) {
            Log.d(TAG, "Async Task onCancelled With Result");
            super.onCancelled(result);
        }
    
        @Override
        protected void onCancelled() {
            Log.d(TAG, "Async Task onCancelled");
            super.onCancelled();
        }
    
        @Override
        protected Void doInBackgroundImpl(Void... params) {
            // Do background work
            return null;
        };
     }
    

    【讨论】:

    • 绝对没有必要仅仅为了安排对cancel()的调用而创建一个新的looper线程。您可以从onPreExecute() 中的主线程执行此操作。另外,如果您手动取消任务,您还应该取消预定的调用以避免泄漏。
    • 这里的要点是在 doInBackground() 中间取消 AsyncTask,因为它在执行而不是在 onPreExecute() 上花费了太多时间,我也想只取消这个 AsyncTask 的实例,这需要太多时间时间并保留其他人,非常感谢您的反馈。
    • 我认为我的信息不够清楚。我没有说你应该在 onPreExecute() 中取消,我说你应该在 onPreExecute() 中创建处理程序并从主线程发布延迟取消。这样你就可以使用主线程作为 looper 线程,当然你可以在 doInBackground() 执行时取消 AsyncTask,因为主线程也和后台线程同时运行。
    【解决方案4】:

    我可以通过添加一条简单的线来解决此类类似问题

    HttpURLConnection hConn = (HttpURLConnection) url.openConnection();
    hConn.setRequestMethod("HEAD");
    

    我的要求是知道响应代码,因此仅获取元信息就足够了,而不是获取完整的响应正文。

    默认的请求方法是 GET,这需要很长时间才能返回,最后抛出了 SocketTimeoutException。当我将请求方法设置为 HEAD 时,响应非常快。

    【讨论】:

    • 这绝不是解决方案,您将请求方法更改为 HEAD 请求,它不会产生任何响应正文。
    • 这不会对原始问题添加任何内容。 OP 在他们的代码中有.setRequestMethod("HEAD")。奇怪的是,这个描述正是我减少“打开的文件太多”问题所需要的。那么谢谢?
    猜你喜欢
    • 2016-01-02
    • 2017-02-18
    • 1970-01-01
    • 2017-12-25
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    相关资源
    最近更新 更多