java 如何在自己的线程中执行 Web 请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2022170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:58:35  来源:igfitidea点击:

How to execute web request in its own thread?

javaandroidmultithreading

提问by Niko Gamulin

I am creating an android application which has to execute web requests in the background and then handle the received data and modify the user interface according to the server response.

我正在创建一个 android 应用程序,它必须在后台执行 Web 请求,然后处理接收到的数据并根据服务器响应修改用户界面。

The goal of posting requests and handling data in the background is to avoid the freezing of user interface. Currently however I notice that the user interface is freezing so I am not sure the logic is working as it is supposed to.

在后台发布请求和处理数据的目的是避免用户界面冻结。但是目前我注意到用户界面正在冻结,所以我不确定逻辑是否按预期工作。

Here is the part of code which is supposed to post requests and handle responses in its own thread and then pass the data to GUI:

这是应该在自己的线程中发布请求和处理响应,然后将数据传递给 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);
}

The method sendRequest() is called from the main activity which implements ServerResponseHandler. I guess the request is executed in its own thread and by calling

从实现 ServerResponseHandler 的主要活动调用方法 sendRequest()。我猜这个请求是在它自己的线程中执行的,并通过调用

responseHandler.onProductsResponse(products);

responseHandler.onProductsResponse(products);

the list of products (data from the web) is passed to main activity. Anyway due to poor performance I would appreciate if anyone could correct any possible issue in the logic above or suggest any other (better) option.

产品列表(来自网络的数据)被传递到主要活动。无论如何,由于性能不佳,如果有人可以纠正上述逻辑中任何可能的问题或提出任何其他(更好的)选项,我将不胜感激。

回答by Reto Meier

I'd suggest you take a look at ASyncTask class(available since Android 1.5).

我建议您查看ASyncTask 类(自 Android 1.5 起可用)。

It simplifies the process of creating a background Thread that synchronizes with the GUI thread once it's complete.

它简化了创建后台线程的过程,该线程在完成后与 GUI 线程同步。

You should be able to achieve what you're trying using code something list this

你应该能够使用代码来实现你正在尝试的东西列出这个

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

And execute by calling

并通过调用执行

new DownloadFilesTask().execute(url1, url2, url3);

回答by tbruyelle

According to the handler javadoc, I don't think the post()method create any threads. If I'm right it execute the Runnableon the thread to which the handler is attached. So in this case this is the activity thread so the UI thread ! That's why you have poor performance.

根据处理程序 javadoc,我认为该post()方法不会创建任何线程。如果我是Runnable对的,它将在处理程序附加到的线程上执行。所以在这种情况下,这是活动线程,所以是 UI 线程!这就是你表现不佳的原因。

You have to implement a Threadwhich execute your Runnable. But by doing that, you won't be able to update your activity like you currently do by calling :

你必须实现一个Thread执行你的Runnable. 但是通过这样做,您将无法像当前那样通过调用来更新您的活动:

responseHandler.onProductsResponse(products);

This is because you are not any more in the UI thread, and only the UI thread is authorized to interact with the UI (so the activity). So you have to replace this call by accessing your Handler.

这是因为您不再处于 UI 线程中,并且只有 UI 线程被授权与 UI 交互(活动也是如此)。因此,您必须通过访问您的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 );

And implementing the handleMessage()method for your Handler:

handleMessage()为您实施该方法Handler

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

Last but not least : the Handlerhas to still be created in the activity thread.

最后但并非最不重要的是:Handler仍然必须在活动线程中创建。