Андроид. Как избежать ANR с помощью HttpClient

У меня есть некоторые проблемы с моим приложением. В отчетах по рынку часто появляется репостр ANR с ошибками HttpClient. Есть

java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2016) в org.apache.http.impl.conn.tsccm.WaitingThread.await(WaitingThread.java:159) в org.apache.http. impl.conn.tsccm.ConnPoolByRoute.getEntryBlocking(ConnPoolByRoute.java:339) в org.apache.http.impl.conn.tsccm.ConnPoolByRoute$1.getPoolEntry(ConnPoolByRoute.java:238) в org.apache.http.impl.conn .tsccm.ThreadSafeClientConnManager$1.getConnection(ThreadSafeClientConnManager.java:175) в org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:325) в org.apache.http.impl.client.AbstractHttpClient.execute( AbstractHttpClient.java:580) в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:512) в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:490)


java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2022) в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:413) в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor. java:1014) в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1074) в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:574) в java.lang.Thread.run(Thread .java:1020)


ПОТОКИ DALVIK: (мьютексы: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0) "main" prio=5 tid=1 NATIVE | group="main" sCount=1 dsCount=0 obj=0x40027550 self=0xcfc0 | sysTid=2557 nice=0 sched=0/0 cgrp=дескриптор по умолчанию=-1345006240 | schedstat=( 6440246597 181026702867 12047 ) в org.apache.harmony.luni.platform.OSNetworkSystem.connect(собственный метод) в dalvik.system.BlockGuard$WrappedNetworkSystem.connect(BlockGuard.java:357) в org.apache.harmony.luni .net.PlainSocketImpl.connect(PlainSocketImpl.java:207) на org.apache.harmony.luni.net.PlainSocketImpl.connect(PlainSocketImpl.java:440) на java.net.Socket.connect(Socket.java:1013) на org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:119) в org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:143) в org.apache.http.impl. conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) в org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) в org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector. java:359) в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttp Client.java:555) в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465

Есть ли способ избежать этой ошибки? Может быть, есть лучшая практика работы с httpClient? В моем приложении я использую:

 public ApiImpl() {
    this.httpClient = new DefaultHttpClient();
    ClientConnectionManager mgr = httpClient.getConnectionManager();
    HttpParams params = httpClient.getParams();
    this.httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, mgr.getSchemeRegistry()), params);
}


public class Client {

private static Api api;
private static Client instance = null;


public static Client getInstance() {
    if (instance == null) {
        instance = new Client();
    }
    return instance;
}

 private Client() {
    api = new ApiImpl();
}

}

то в коде я использую следующее

Client client = Client.getInstance();
client.do();

person Georgy Gobozov    schedule 13.10.2011    source источник
comment
сообщает ли он об ошибке при вызове getInstance() или при выполнении http-запроса?   -  person Blackbelt    schedule 13.10.2011
comment
это потому, что этот вызов выполнения httpclient является блокирующим вызовом. Поток пользовательского интерфейса кажется заблокированным из-за задержки выполнения. Вам нужно запустить выполнение и каждый блокирующий вызов в отдельном потоке или асинхронной задаче.   -  person Blackbelt    schedule 13.10.2011
comment
Нет, я использую обработчик и запускаю для выполнения запроса. Когда это сделано, я отправляю сообщение в основной поток. Звоню один раз - все ок, звоню - два раза - ок, звоню три раза   -  person Georgy Gobozov    schedule 14.10.2011
comment
Хорошо ... один и тот же http-клиент используется несколькими потоками или у вас есть основной поток и ваш поток? в последнем попробуйте использовать простой http-клиент по умолчанию вместо реализации потокобезопасного и посмотрите, изменится ли что-то   -  person Blackbelt    schedule 14.10.2011


Ответы (2)


Вы, вероятно, вызываете этот последний фрагмент из основного потока пользовательского интерфейса в своей деятельности. Если это так, вам следует рассмотреть возможность выполнения операции с потенциально высокой задержкой, такой как HttpClient.execute, в другом потоке. Вы можете просто использовать другой поток или исполнителя.

Если вам нужно согласовать сетевой запрос с пользовательским интерфейсом, попробуйте AsyncTask или Загрузчики.

person kramimus    schedule 13.10.2011
comment
Нет, я использую обработчик и запускаю для выполнения запроса. Когда это сделано, я отправляю сообщение в основной поток. Звонишь один раз - все ок, звонишь - два - ок, трижды звонишь - ANR. - person Georgy Gobozov; 14.10.2011
comment
Если вы создаете обработчик в основном потоке, ваши исполняемые Runnables все еще выполняются в основном потоке. Обработчик — это просто очередь сообщений, которая выполняется в том же потоке, в котором она была создана. - person kramimus; 14.10.2011
comment
Как правильно использовать обработчик в другом потоке? новый Thread(myRunnable).start() вместо handler.post(myRunnable), верно? - person Georgy Gobozov; 15.10.2011
comment
Да, проще всего было бы создать новую тему, как вы сказали. Затем, если вы хотите обновить пользовательский интерфейс из этого потока позже, вы должны опубликовать что-то в своем обработчике, чтобы выполнить обновление. Кроме того, вы можете использовать вспомогательный класс, такой как AsyncTask, для координации обновлений пользовательского интерфейса и фоновых сетевых задач. - person kramimus; 16.10.2011

Проблема заключалась в том, что я не прочитал весь текст ответа. Я просто проверяю, является ли ответ нулевым или нет, если я ожидал логического результата. В любом случае вы должны прочитать тело ответа и закрыть поток ответов перед следующим вызовом экземпляра http-клиента. Например

  InputStream stream = response.getEntity().getContent();

  private String streamToString(InputStream is) {
        if (is == null) return null;
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
person Georgy Gobozov    schedule 23.08.2012