Paramètres de délai d'expiration de connexion HttpURLConnection

123

Je veux renvoyer false si l'URL prend plus de 5 secondes pour se connecter - comment est-ce possible en utilisant Java? Voici le code que j'utilise pour vérifier si l'URL est valide

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

Réponses:

201

HttpURLConnectiona une méthode setConnectTimeout .

Réglez simplement le délai d'expiration à 5000 millisecondes, puis attrapez java.net.SocketTimeoutException

Votre code devrait ressembler à ceci:


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
la source
3
J'ai mis la valeur à 10 minutes. Cependant, il me jette un java.net.ConnectException: Connection timed out: connectavant même 2 minutes est écoulé. Savez-vous ce qui cause le problème?
Pacerier
5
SocketTimeoutException est une sous-classe de IOException. Si les deux blocs catch font la même chose, vous pouvez simplement intercepter IOException.
spaaarky21
2
@ spaaarky21 est correct. Si toutefois vous créez une interface utilisateur et que vous souhaitez informer vos utilisateurs qu'un délai d'expiration s'est produit, vous devez intercepter SocketTimeoutException avant IOException, sinon, il sera inaccessible.
Clocker
3
NB !!! vous devez appeler setConnectTimeoutavant l'une des méthodes qui se connectent implicitement (essentiellement toutes les méthodes qui lancent IllegalStateException si elles sont déjà connectées). Idéalement, faites de setConnectTimeout (readTimeout) les premières méthodes appelées.
Adam Gent
4
Cela n'a pas fonctionné pour moi. Mais, après avoir ajouté con.setReadTimeout(), cela a fonctionné comme prévu.
Paulo
115

Vous pouvez définir un délai comme celui-ci,

con.setConnectTimeout(connectTimeout);
con.setReadTimeout(socketTimeout);
Codeur ZZ
la source
2
Quelle est la valeur maximale du délai d'expiration que nous pouvons spécifier?
Pacerier
7
@Pacerier La documentation ne l'indique pas explicitement. Il lève une IllegalArgumentException si la valeur est négative (une valeur de 0 signifierait attendre indéfiniment). Étant donné que le délai d'expiration est un int 32 bits non signé, je suppose que le délai maximal serait d'environ 49 jours (bien que je doute sérieusement qu'une telle valeur soit utile à quiconque).
Jay Sidri
1

Si la connexion HTTP n'expire pas, vous pouvez implémenter le vérificateur de délai d'expiration dans le thread d'arrière-plan lui-même (AsyncTask, Service, etc.), la classe suivante est un exemple de Customize AsyncTask qui expire après une certaine période

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

Un échantillon pour cela

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
la source
Il est absolument inutile de créer un nouveau thread de boucleur juste pour planifier un appel à cancel (). Vous pouvez le faire à partir du thread principal dans onPreExecute(). De plus, si vous annulez la tâche manuellement, vous devez également annuler l'appel programmé pour éviter les fuites.
BladeCoder
Le point ici est d'annuler AsyncTask au milieu de doInBackground () quand cela prend trop de temps à l'exécution et non à onPreExecute (), aussi je veux annuler uniquement cette instance d'AsyncTask qui prend trop de temps et garder les autres, beaucoup apprécier vos réactions.
Ayman Mahgoub
2
Je pense que mon message n'était pas assez clair. Je n'ai pas dit que vous devriez annuler dans onPreExecute (), j'ai dit que vous devriez créer le gestionnaire dans onPreExecute () et publier l'annulation retardée à partir du thread principal. De cette façon, vous utiliserez le thread principal comme thread de boucleur et vous pourrez bien sûr annuler l'AsyncTask plus tard pendant que doInBackground () s'exécute car le thread principal s'exécute également en même temps que le thread d'arrière-plan.
BladeCoder
-1

Je pourrais trouver une solution à un problème similaire en ajoutant une simple ligne

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

Mon exigence était de connaître le code de réponse et pour cela, il suffisait d'obtenir les méta-informations au lieu d'obtenir le corps de la réponse complet.

La méthode de demande par défaut est GET et cela prenait beaucoup de temps à revenir, me jetant finalement SocketTimeoutException. La réponse a été assez rapide lorsque j'ai défini la méthode de demande sur HEAD.

Subrata Nath
la source
1
Ce n'est en aucun cas la solution, vous changez la méthode de HEADrequête en une requête qui ne produira aucun corps de réponse.
Sveinung Kval Bakken
Cela n'ajoute rien à la question initiale. OP a .setRequestMethod("HEAD")dans son code. Curieusement, cette description était exactement ce dont j'avais besoin pour réduire mon problème de «Trop de fichiers ouverts». Donc merci?
Joshua Pinter