Статьи

HTTP-клиент Android: ошибка сети и ConnectivityManager

В этом посте я покажу, как обрабатывать ошибки соединения или ситуацию, когда соединение меняется (т.е. с UMTS на WI-FI). Во всех этих случаях мы должны корректно обрабатывать эти ошибки и реализовывать некоторые стратегии, чтобы попытаться восстановить соединение, если это возможно.

В идеальном мире, когда мы подключаемся к удаленному серверу по протоколу HTTP, все работает отлично, нам не нужно беспокоиться об ошибках подключения, о сервере с большой нагрузкой или нестабильной сети. В некоторых из последних постов мы говорили о том, как мы можем подключиться к удаленному серверу и как мы можем отправлять и получать данные. В реальном мире, обычно, когда мы подключены к удаленному серверу, мы ходим или бежим, или даже находимся в машине, так что сетевой сигнал может измениться или внезапно сломаться. В то же время Android может переключаться с UMTS на WI-FI или наоборот, или он может переключаться с одной APN на другую и так далее. Когда мы кодируем наше приложение, мы должны учитывать все эти ситуации и правильно их обрабатывать. Мы можем упростить и попытаться классифицировать все эти события в двух классах:

  • Сетевая ошибка
  • Изменения конфигурации сети

Если бы мы использовали Apache HTTP Client, уже есть механизм, который нам помогает, мы могли бы реализовать интерфейс HttpRequestRetryHandler с нашей логикой механизма повторных попыток или реализацией по умолчанию. Если мы используем стандартное Android-соединение HTTP, мы должны реализовать его сами.

Обработка ошибки соединения

В этом посте я показал, как подключиться к удаленному серверу, и мы создали класс HttpClient, который управляет соединением. В этом случае мы расширяем этот класс, обрабатывая возможные сетевые ошибки. Для этого мы используем механизм обратного вызова, другими словами, мы настраиваем прослушиватель, который получает уведомление при возникновении ошибки соединения. Для этого мы создаем интерфейс:

1
2
3
4
public interface HttpConnectionRetryHandler {
 
    public boolean shouldRetry(Throwable t, int attemptNumber) ;
}

Этот интерфейс имеет только один метод, в котором мы реализуем нашу логику для обработки или нет сетевой ошибки. Мы передаем исключение и счетчик, который содержит количество попыток восстановить соединение. Затем необходимо изменить HttpClient для обработки ошибки подключения. Он не должен вызывать исключение, как только он не может подключиться к серверу, но должен спросить наш класс, который реализует HttpConnectionRetryHandler, если он должен вызвать исключение или попытаться подключиться снова.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public void connect(String method, Properties props) throws HttpClientException {
    boolean status  = true;
    int attemptNumber = 0;
    while (status) {
        try {
            attemptNumber++;
            doConnection(method, props);
            status = false;
        }
        catch(Throwable t) {
            if (handler != null) {
                status = handler.shouldRetry(t, attemptNumber);
                if (!status)
                    throw new HttpClientException(t);
            }
            else {                   
                throw new HttpClientException(t);
            }
        }
    }
 
}

В строке 12, когда у нас есть исключение, мы вызываем handler.shouldRetry, чтобы проверить, хочет ли наш обработчик ошибок соединения обработать ошибку или нет. Если нет, мы выдвигаем исключение, в противном случае мы продолжаем пытаться восстановить соединение. В нашем классе, который обрабатывает ошибку, мы реализуем нашу бизнес-логику. Самая простая вещь — проверка количества попыток. В этом случае мы имеем:

01
02
03
04
05
06
07
08
09
10
11
12
public class DefaultHttpConnectionRetryHandler implements HttpConnectionRetryHandler {
 
    @Override
    public boolean shouldRetry(Throwable t, int attemptNumber) {
        System.out.println("Attempt ["+attemptNumber+"]");
        if (attemptNumber > 5)
            return false;
 
        return true;
    }
 
}

Поэтому, когда мы вызываем HttpClient, мы имеем:

1
2
3
...
client.setHandler(new DefaultHttpConnectionRetryHandler());
...

Обработка изменений конфигурации сети

Как мы уже говорили ранее, когда мы перемещаемся со своим смартфоном или планшетом, конфигурация сети может случайно переключиться с WI-FI на UMTS или наоборот или с UTMS на переключение с одной APN на другую (возможно, в роуминге). Мы должны обработать все эти изменения и попытаться использовать сетевые конфигурации для восстановления соединения. Для обработки этих событий мы можем использовать приемник Broadcast, который получает уведомление, когда происходит изменение конфигурации сети. Изменения в конфигурации APN или в изменениях в подключении обрабатываются ConnectivityManager, который передает некоторую информацию своим подписчикам. Итак, первое, что мы кодируем наш приемник вещания:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ConnectionMonitorReceiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context ctx, Intent intent) {
        System.out.println("Receive..");
        NetworkInfo netInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
 
        if (netInfo == null)
            return ;
 
        System.out.println("Here");
        if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
            String proxyHost = System.getProperty( "http.proxyHost" );
 
            String proxyPort = System.getProperty( "http.proxyPort" );
 
            if (proxyHost != null)
                HttpClient.getInstance().setProxy(proxyHost, proxyPort);
            else
                HttpClient.getInstance().setProxy(null, null);
 
        }
    }
}

В строке 1 мы просто расширяем BroadcastReceiver и реализуем метод onReceive (строка 4). В строке 6 мы получаем информацию, отправленную в рамках широкого намерения, и реализуем нашу логику. В нашем случае мы просто проверяем, изменился ли прокси, но мы могли бы реализовать другой тип бизнес-логики.

Последнее, что нам нужно сделать, — это зарегистрировать наш приемник вещания, чтобы он мог быть вызван. Мы делаем это с помощью метода onCreate в Activity:

1
registerReceiver(new ConnectionMonitorReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

Запустив код мы имеем:

Immagine1 Immagine2

Ссылка: Android HTTP Client: ошибка сети и ConnectivityManager от нашего партнера JCG Франческо Аццолы из блога Surviving w / Android .