Как выполнить веб-запрос в своем потоке?
Я создаю приложение для 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(продукты);
список продуктов (данные из Интернета) передается основной деятельности. В любом случае из-за низкой производительности я был бы признателен, если бы кто-нибудь мог исправить любую возможную проблему в логике выше или предложить любую другую (лучшую) опцию.
Ответы
Ответ 1
Я бы посоветовал вам взглянуть на класс ASyncTask (доступный с 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);
Ответ 2
В соответствии с обработчик 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
.