Ustawienia limitu czasu HttpURLConnection

123

Chcę zwrócić wartość false, jeśli połączenie z adresem URL trwa dłużej niż 5 sekund - jak to możliwe za pomocą języka Java? Oto kod, którego używam, aby sprawdzić, czy adres URL jest prawidłowy

HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
Malachiasz
źródło

Odpowiedzi:

201

HttpURLConnectionma metodę setConnectTimeout .

Po prostu ustaw limit czasu na 5000 milisekund, a następnie złap java.net.SocketTimeoutException

Twój kod powinien wyglądać mniej więcej tak:


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;
}


dbyrne
źródło
3
Ustawiłem wartość na 10 minut. Jednak rzuca mi to, java.net.ConnectException: Connection timed out: connectzanim miną nawet 2 minuty. Czy wiesz, co jest przyczyną problemu?
Pacerier,
5
SocketTimeoutException jest podklasą IOException. Jeśli oba bloki catch robią to samo, możesz po prostu złapać IOException.
spaaarky21
2
@ spaaarky21 jest poprawne. Jeśli jednak tworzysz interfejs użytkownika i chcesz powiadomić użytkowników o przekroczeniu limitu czasu, musisz przechwycić SocketTimeoutException przed IOException, jeśli nie, będzie nieosiągalny.
Clocker
3
NB !!! musisz wywołać setConnectTimeoutprzed którąkolwiek z metod, które niejawnie łączą się (w zasadzie wszystkie metody, które zgłaszają IllegalStateException, jeśli są już połączone). Idealnie byłoby ustawić setConnectTimeout (readTimeout) jako pierwsze wywoływane metody.
Adam Gent,
4
Dla mnie to nie zadziałało. Ale po dodaniu con.setReadTimeout()działało zgodnie z oczekiwaniami.
Paulo,
115

Możesz ustawić limit czasu w ten sposób,

con.setConnectTimeout(connectTimeout);
con.setReadTimeout(socketTimeout);
ZZ Coder
źródło
2
Jaka jest maksymalna wartość limitu czasu, którą możemy określić?
Pacerier,
7
@Pacerier Dokumentacja nie podaje tego wprost. Rzuca wyjątek IllegalArgumentException, jeśli wartość jest ujemna (wartość 0 oznacza oczekiwanie w nieskończoność). Ponieważ limit czasu jest 32-bitowym intem bez znaku, przypuszczam, że maksymalny limit czasu wyniesie około 49 dni (choć poważnie wątpię, czy taka wartość byłaby pomocna dla każdego).
Jay Sidri,
1

Jeśli połączenie HTTP nie przekracza limitu czasu, możesz zaimplementować sprawdzanie limitu czasu w samym wątku w tle (AsyncTask, Service itp.), Poniższa klasa jest przykładem dla Dostosuj AsyncTask, który limit czasu po pewnym okresie

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);
}

Próbka do tego

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;
    };
 }
Ayman Mahgoub
źródło
Całkowicie niepotrzebne jest tworzenie nowego wątku looper tylko po to, aby zaplanować wywołanie cancel (). Możesz to zrobić z głównego wątku w onPreExecute(). Ponadto, jeśli anulujesz zadanie ręcznie, powinieneś również anulować zaplanowane połączenie, aby uniknąć wycieków.
BladeCoder
Chodzi o to, aby anulować AsyncTask w środku doInBackground (), gdy wykonanie zajmuje zbyt dużo czasu, a nie onPreExecute (), również chcę anulować tylko to wystąpienie AsyncTask, które zajmuje zbyt dużo czasu i zachowuje inne, bardzo doceniam swoją opinię.
Ayman Mahgoub
2
Myślę, że moje przesłanie nie było wystarczająco jasne. Nie powiedziałem, że powinieneś anulować w onPreExecute (), powiedziałem, że powinieneś utworzyć Handler w onPreExecute () i opublikować opóźnione anulowanie z głównego wątku. W ten sposób użyjesz głównego wątku jako wątku chwytacza i możesz oczywiście anulować AsyncTask później, podczas wykonywania doInBackground (), ponieważ główny wątek również działa równolegle z wątkiem w tle.
BladeCoder
-1

Mogłem rozwiązać podobny problem dodając prostą linię

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

Moim wymaganiem była znajomość kodu odpowiedzi, a do tego wystarczyło samo uzyskanie metainformacji, zamiast pełnej treści odpowiedzi.

Domyślną metodą żądania jest GET, a jej powrót zajmował dużo czasu, w końcu rzucając mi SocketTimeoutException. Odpowiedź była dość szybka, gdy ustawiłem metodę żądania na HEAD.

Subrata Nath
źródło
1
W żaden sposób nie jest to rozwiązanie, zmieniasz metodę żądania na HEADżądanie, które nie wygeneruje żadnej treści odpowiedzi.
Sveinung Kval Bakken
To nic nie dodaje do pierwotnego pytania. OP ma .setRequestMethod("HEAD")w swoim kodzie. Co dziwne, ten opis był dokładnie tym, czego potrzebowałem, aby zmniejszyć problem „Zbyt wiele otwartych plików”. Więc dziękuję?
Joshua Pinter