Как выполнить веб-запрос в собственном потоке? - PullRequest
11 голосов
/ 07 января 2010

Я создаю приложение для Android, которое должно выполнять веб-запросы в фоновом режиме, а затем обрабатывать полученные данные и изменять пользовательский интерфейс в соответствии с ответом сервера.

Целью публикации запросов и обработки данных в фоновом режиме является предотвращение зависания пользовательского интерфейса. Однако в настоящее время я замечаю, что пользовательский интерфейс зависает, поэтому я не уверен, что логика работает так, как предполагалось.

Вот часть кода, которая должна отправлять запросы и обрабатывать ответы в своем собственном потоке, а затем передавать данные в GUI:

public class ServerConnection {

Queue<String> requests;

...

DefaultHttpClient httpClient;
HttpHost targetHost;

Handler handler;

ServerResponseHandler responseHandler;
Activity activity;

public ServerConnection(Activity activity){
    this.activity = activity;
    this.responseHandler = (ServerResponseHandler) activity;
    httpClient = new DefaultHttpClient();
    targetHost = new HttpHost(TARGET_DOMAIN, 80, "http");
    requests = new LinkedList<String>();
}



private Runnable requestSender = new Runnable(){

    @Override
    public void run() {
        if(!requests.isEmpty()){
            String requestString = requests.remove();
            HttpGet httpGet = new HttpGet(requestString);
            httpGet.addHeader("Accept", "text/xml");
            String encodingString = "testuser:testpass";
            String sEncodedString = Base64Coder.encodeString(encodingString);

            try{

                String sContent = fetchURL(requestString, sEncodedString);

                XMLParser xmlParser = new XMLParser();

                List <Product> products = xmlParser.getProducts(sContent);

                responseHandler.onProductsResponse(products);
            }
            catch(Exception ex){
                Log.e(TAG, ex.getMessage());
            }
        }
    }
};

public void sendRequest(String requestString){
    requests.add(requestString);
    handler = new Handler();
    handler.post(requestSender);
}

Метод sendRequest () вызывается из основного действия, которое реализует ServerResponseHandler. Я предполагаю, что запрос выполняется в своем собственном потоке и вызывает

responseHandler.onProductsResponse (продукты);

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

Ответы [ 2 ]

21 голосов
/ 07 января 2010

Я бы посоветовал вам взглянуть на ASyncTask class (доступно с Android 1.5).

Упрощает процесс создания фонового потока, который синхронизируется с потоком GUI после его завершения.

Вы должны быть в состоянии достичь того, что вы пытаетесь с помощью кода, перечислите это

private class DownloadFilesTask extends AsyncTask<String, List<Product>, Integer> {
     protected List<Products> doInBackground(String... requestStrings) {
        int count = requestStrings.length;
        int results = 0;
        for (int i = 0; i < count; i++) {
          String requestString = requestStrings[i];
          HttpGet httpGet = new HttpGet(requestString);
          httpGet.addHeader("Accept", "text/xml");
          String encodingString = "testuser:testpass";
          String sEncodedString = Base64Coder.encodeString(encodingString);
          try{
            String sContent = fetchURL(requestString, sEncodedString);
            XMLParser xmlParser = new XMLParser();
            List <Product> products = xmlParser.getProducts(sContent);
            results++;
            publishProgress(products);
          }
          catch(Exception ex){
            Log.e(TAG, ex.getMessage());
          }
        }
        return results;
     }

     protected void onProgressUpdate(Integer... progress) {
         // TODO You are on the GUI thread, and the first element in 
         // the progress parameter contains the last progress
         // published from doInBackground, so update your GUI
     }

     protected void onPostExecute(int result) {
       // Processing is complete, result contains the number of 
       // results you processed
     }
 }

И выполнить, вызвав

new DownloadFilesTask().execute(url1, url2, url3);
0 голосов
/ 07 января 2010

Согласно обработчику javadoc , я не думаю, что метод post() создает какие-либо потоки. Если я прав, то выполните Runnable в потоке, к которому прикреплен обработчик. Так что в этом случае это поток активности, поэтому поток пользовательского интерфейса! Вот почему у вас низкая производительность.

Вы должны реализовать Thread, который выполняет ваш Runnable. Но, сделав это, вы не сможете обновить свою активность, как в настоящее время, позвонив по номеру:

responseHandler.onProductsResponse(products);

Это потому, что вы больше не участвуете в потоке пользовательского интерфейса, и только поток пользовательского интерфейса имеет право взаимодействовать с пользовательским интерфейсом (таким образом, действием). Таким образом, вы должны заменить этот вызов, получив доступ к Handler.

Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putSerializable( "products", products ); //not sure it will pass here 
msg.setData( bundle );
handler.sendMessage( msg );

И реализация метода handleMessage() для вашего Handler:

@Override
public void handleMessage( Message msg )
{
  List <Product> products = msg.getData().getSerializable( "products" );
  responseHandler.onProductsResponse(products);
}

Последнее, но не менее важное: Handler все еще должен быть создан в цепочке действий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...