2012-02-29 18 views
19

मैं निम्न समस्या से जूझ रहा हूं: मेरा ऐप एचटीपी क्लाइंट का उपयोग कर http सर्वर के अनुरोधों का अनुक्रम बनाता है। मैं सर्वर पर डेटा भेजने के लिए HttpPut का उपयोग करता हूं। पहला अनुरोध अच्छी तरह से और तेज़ हो जाता है, दूसरा अनुरोध 40 सेकंड के लिए लटकता है और फिर मैं कनेक्शन टाइम अपवाद को पकड़ता हूं। मैं अपने एचटीपी क्लाइंट का पुन: उपयोग करने की कोशिश कर रहा हूं और उसी उदाहरण के माध्यम से दूसरा अनुरोध भेज रहा हूं। यदि मैं नए कनेक्शन प्रबंधक के साथ एक साथ नया HttpClient बना देता हूं, तो सबकुछ ठीक काम करता है।एंड्रॉइड httpclient सर्वर के दूसरे अनुरोध पर लटकता है (कनेक्शन का समय समाप्त हो गया है)

ऐसा क्यों हो रहा है? और इसे कैसे ठीक करें और हर बार नया HttpClient न बनाएं?

अग्रिम धन्यवाद। अगर मैं टिप्पणी readClient = newHttpClient (readClient) doPut में, तो समस्या पैदा होती है (

public class WebTest 
{ 
private HttpClient readClient; 
private SchemeRegistry httpreg; 
private HttpParams params; 

private URI url; //http://my_site.net/data/ 

protected HttpClient newHttpClient(HttpClient oldClient) 
{ 
    if(oldClient != null) 
     oldClient.getConnectionManager().shutdown(); 

    ClientConnectionManager cm = new SingleClientConnManager(params, httpreg); 
    return new DefaultHttpClient(cm, params); 
} 

protected String doPut(String data) 
{ 
    //**************************** 
    //Every time we need to send data, we do new connection 
    //with new ConnectionManager and close old one 
    readClient = newHttpClient(readClient); 

    //***************************** 


    String responseS = null; 
    HttpPut put = new HttpPut(url); 
    try 
    { 
     HttpEntity entity = new StringEntity(data, "UTF-8"); 
     put.setEntity(entity); 
     put.setHeader("Content-Type", "application/json; charset=utf-8"); 
     put.setHeader("Accept", "application/json"); 
     put.setHeader("User-Agent", "Apache-HttpClient/WebTest"); 

     responseS = readClient.execute(put, responseHandler); 
    } 
    catch(IOException exc) 
    { 
     //error handling here 
    } 
    return responseS; 
} 

public WebTest() 
{ 
    httpreg = new SchemeRegistry(); 
    Scheme sch = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80); 
    httpreg.register(sch); 

    params = new BasicHttpParams(); 
    ConnPerRoute perRoute = new ConnPerRouteBean(10); 
    ConnManagerParams.setMaxConnectionsPerRoute(params, perRoute); 
    ConnManagerParams.setMaxTotalConnections(params, 50); 
    ConnManagerParams.setTimeout(params, 15000); 
    int timeoutConnection = 15000; 
    HttpConnectionParams.setConnectionTimeout(params, timeoutConnection); 
    // Set the default socket timeout (SO_TIMEOUT) 
    // in milliseconds which is the timeout for waiting for data. 
    int timeoutSocket = 40000; 
    HttpConnectionParams.setSoTimeout(params, timeoutSocket); 
} 

private ResponseHandler<String> responseHandler = new ResponseHandler<String>() 
{ 
    @Override 
    public String handleResponse(HttpResponse response) 
      throws ClientProtocolException, IOException 
    { 
     StatusLine statusLine = response.getStatusLine(); 
     if (statusLine.getStatusCode() >= 300) 
     { 
      throw new HttpResponseException(statusLine.getStatusCode(), 
        statusLine.getReasonPhrase()); 
     } 

     HttpEntity entity = response.getEntity(); 
     if(entity == null) 
      return null; 

     InputStream instream = entity.getContent(); 
     return this.toString(entity, instream, "UTF-8"); 
    } 

    public String toString(
      final HttpEntity entity, 
      final InputStream instream, 
      final String defaultCharset) throws IOException, ParseException 
    { 
     if (entity == null) 
     { 
      throw new IllegalArgumentException("HTTP entity may not be null"); 
     } 

     if (instream == null) 
     { 
      return null; 
     } 
     if (entity.getContentLength() > Integer.MAX_VALUE) 
     { 
      throw new IllegalArgumentException("HTTP entity too large to be buffered in memory"); 
     } 
     int i = (int)entity.getContentLength(); 
     if (i < 0) 
     { 
      i = 4096; 
     } 
     String charset = EntityUtils.getContentCharSet(entity); 
     if (charset == null) 
     { 
      charset = defaultCharset; 
     } 
     if (charset == null) 
     { 
      charset = HTTP.DEFAULT_CONTENT_CHARSET; 
     } 

     Reader reader = new InputStreamReader(instream, charset); 

     StringBuilder buffer=new StringBuilder(i); 
     try 
     { 
      char[] tmp = new char[1024]; 
      int l; 
      while((l = reader.read(tmp)) != -1) 
      { 
       buffer.append(tmp, 0, l); 
      } 
     } finally 
     { 
      reader.close(); 
     } 

     return buffer.toString(); 
    } 
}; 

}

+0

सर्वर अपने कनेक्शन को बंद किया जा सकता है। प्रतिक्रिया शीर्षलेख क्या हैं? –

+0

सर्वर को मेरा दूसरा अनुरोध भी प्राप्त नहीं होता –

+2

उपभोग सामग्री() उत्तर है, –

उत्तर

13

अजीब लगता है, लेकिन मैं ठीक उसी समस्या थी:

यहाँ मेरी कोड है।। जिस ऐप पर मैं काम कर रहा था, वह सूची दृश्य में प्रदर्शित करने के लिए थंबनेल छवियों का एक समूह डाउनलोड करने के लिए कई लगातार अनुरोध कर रहा था, और दूसरे के बाद यह लटका होगा जैसे एचटीपी क्लाइंट कोड में एक मृत लॉक था।

अजीब ठीक है कि मैंने पाया कि उपयोग किया गया था DefaultHttpClient के बजाय AndroidHttpClient। जैसे ही मैंने यह किया, और मैंने इस मार्ग पर जाने से पहले बहुत सी चीजों की कोशिश की, यह ठीक काम करना शुरू कर दिया। अनुरोध के साथ पूरा होने पर बस क्लाइंट.क्लोज़() को कॉल करना याद रखें।

AndroidHttpClient को दस्तावेज़ में डिफ़ॉल्ट एचटीपी क्लाइंट के रूप में वर्णित किया गया है "उचित डिफ़ॉल्ट सेटिंग्स और एंड्रॉइड के लिए पंजीकृत योजनाएं"। चूंकि यह एपीआई स्तर 8 (एंड्रॉइड 2.2) में पेश किया गया था, इसलिए मैंने इन "डिफ़ॉल्ट सेटिंग्स" को डुप्लिकेट करने के लिए स्रोत खोला ताकि मैं इसे उस एपीआई स्तर से आगे पीछे इस्तेमाल कर सकूं। यहाँ सुरक्षित रूप से बंद करने के लिए एक स्थिर विधि के साथ चूक और एक सहायक वर्ग डुप्लिकेट करने के लिए मेरे कोड है यह

public class HttpClientProvider { 

    // Default connection and socket timeout of 60 seconds. Tweak to taste. 
    private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000; 

    public static DefaultHttpClient newInstance(String userAgent) 
    { 
     HttpParams params = new BasicHttpParams(); 

     HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
     HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
     HttpProtocolParams.setUseExpectContinue(params, true); 

     HttpConnectionParams.setStaleCheckingEnabled(params, false); 
     HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT); 
     HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT); 
     HttpConnectionParams.setSocketBufferSize(params, 8192); 

     SchemeRegistry schReg = new SchemeRegistry(); 
     schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
     schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); 
     ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg); 

     DefaultHttpClient client = new DefaultHttpClient(conMgr, params); 

     return client; 
    } 

} 

और एक और कक्षा में ...

public static void safeClose(HttpClient client) 
{ 
    if(client != null && client.getConnectionManager() != null) 
    { 
     client.getConnectionManager().shutdown(); 
    } 
} 
+0

बहुत बहुत धन्यवाद, मैं इस विधि को आजमाउंगा। क्या आप इन परिवर्तनों के बाद अपने सभी अनुरोधों के लिए एक उदाहरण का उपयोग करते हैं या हर बार कॉल करते हैं, जब भी आपको डेटा भेजने की आवश्यकता होती है? और क्या आप प्रत्येक अनुरोध के बाद सुरक्षित बंद करते हैं? –

+0

हाँ ... हर बार नया उदाहरण, और सुरक्षित हर बार बंद करें। मुझे कोई प्रदर्शन हिट नहीं दिख रहा है, और यह ऐप मैं उस समय कर रहा था जब मुझे पहली बार यह समस्या सेवा अनुरोधों का एक टन बनाती थी। – Rich

+0

यदि मैं वही करता हूं, तो हर बार नया उदाहरण बंद करें और खोलें, तो समस्या गायब हो जाती है। लेकिन मैंने दस्तावेज़ों में पढ़ा है कि एचटीपी क्लाइंट + कॉनमेनगर का पुन: उपयोग करने की दृढ़ता से अनुशंसा की जाती है और इसे हर कनेक्शन के लिए नहीं बनाते हैं, जो बहुत समय और सीपीयू बचा लेता है ... क्या मैं गलत हूं? –

6

मैं एक ही परेशानी है, जब एक लूप में कई अनुरोध निष्पादित।

आप response.getEntity() के सभी पढ़कर इसे हल कर सकते हैं।

+0

अनुरोध से कॉमसमिंग सामग्री के साथ नहीं है, तो आप यह कैसे करते हैं? – CACuzcatlan

+0

आपको अंत में स्ट्रीम पढ़नी चाहिए, उदाहरण के लिए इनपुटस्ट्रीम = respond.getEntity(); जबकि (is.read()! = - 1); – wilddev

24

लगता है जैसे आप प्रतिक्रिया को संभालने के बाद इकाई को उपभोग करते हैं। आप finally ब्लॉक में निम्न कोड डालें सुनिश्चित करें:

if (httpEntity != null) { 
    try { 
     httpEntity.consumeContent(); 
    } catch (IOException e) { 
     Log.e(TAG, "", e); 
    } 
} 

मैं सुझाव है कि आप पढ़ा HttpClient Tutorial

+0

यह मेरी समस्या हल हो गया, वास्तव में आपको धन्यवाद –

+2

यह सही जवाब होना चाहिए! समस्या सुलझ गयी! – GMsoF

+0

ग्रेट उत्तर। बिंदु पर और एक ट्यूटोरियल के साथ बैक अप। –

1

मुझे भी यही समस्या है। मैं सभी सामग्री का उपभोग कर रहा हूँ।

मैं क्या पाया है कि अगर मैं एक अनुरोध जारी करने के बाद एक कचरा संग्रहण करते हैं, सब कुछ बंद करके एक नया AndroidHttpClient बनाए बिना काम करता है:()

System.gc;

3

मैंने सोचा कि मैं अन्य उत्तरों पर विस्तृत जानकारी दूंगा। मैंने भी इस समस्या का अनुभव किया।समस्या इसलिए थी क्योंकि मैं सामग्री का उपभोग नहीं कर रहा था।

ऐसा लगता है कि यदि आप तब कनेक्शन नहीं करेंगे और आप इस कनेक्शन के साथ एक नया अनुरोध नहीं भेज पाएंगे। मेरे लिए यह विशेष रूप से कठिन बग था क्योंकि मैं एंड्रॉइड में BasicResponseHandler प्रदान कर रहा था। कोड इस तरह दिखता है ...

public String handleResponse(final HttpResponse response) 
      throws HttpResponseException, IOException { 
     StatusLine statusLine = response.getStatusLine(); 
     if (statusLine.getStatusCode() >= 300) { 
      throw new HttpResponseException(statusLine.getStatusCode(), 
        statusLine.getReasonPhrase()); 
     } 

     HttpEntity entity = response.getEntity(); 
     return entity == null ? null : EntityUtils.toString(entity); 
    } 

तो यदि 300 से ऊपर की स्थिति रेखा है तो मैं सामग्री का उपभोग नहीं करता हूं। और मेरे मामले में सामग्री थी। मैंने अपनी खुद की कक्षा बनाई है ...

public class StringHandler implements ResponseHandler<String>{ 

    @Override 
    public BufferedInputStream handleResponse(HttpResponse response) throws IOException { 
    public String handleResponse(final HttpResponse response) 
       throws HttpResponseException, IOException { 
      StatusLine statusLine = response.getStatusLine(); 
      HttpEntity entity = response.getEntity(); 
      if (statusLine.getStatusCode() >= 300) { 
       if (entity != null) { 
        entity.consumeContent(); 
       } 
       throw new HttpResponseException(statusLine.getStatusCode(), 
         statusLine.getReasonPhrase()); 
      } 


      return entity == null ? null : EntityUtils.toString(entity); 
     } 
    } 

} 

तो मूल रूप से किसी भी मामले में सामग्री का उपभोग करें!

+0

असल में प्रतिक्रिया कोड प्रासंगिक नहीं हो सकता है; आपको परवाह किए बिना सामग्री का उपभोग करने का प्रयास करना चाहिए। जैसे मैं छवियों को डाउनलोड कर रहा था, लेकिन सामग्री के रूप में 404 प्रतिक्रिया और एक "सौजन्य छवि" मिलेगा! 500 प्रतिक्रियाएं प्राप्त करते समय यह भी मामला है, अधिकांश सर्वर उस के लिए "पृष्ठ" प्रदान करना चाहते हैं, जो सामग्री के रूप में गिना जाता है। –

1

यह समस्या समाधान के लिए पर्याप्त है (मैं एक ही था):

EntityUtils.consume(response.getEntity()); 

अशक्त जांच के अंदर प्रदर्शन किया

+2

कोई EntityUtils.consume विधि नहीं है? मैं केवल entity.consumeContent() को कॉल कर सकता हूं; – AFD

1

उपभोग इन उत्तरों के कई के बाद से पुराने हैं और अब depricated consumeContent() विधि पर निर्भर करते हैं, मैं सोचा कि मैं Timeout waiting for connection from pool की समस्या के विकल्प के साथ जवाब दूंगा।

HttpEntity someEntity = response.getEntity(); 

    InputStream stream = someEntity.getContent(); 
    BufferedReader rd = new BufferedReader(new InputStreamReader(stream)); 

    StringBuffer result = new StringBuffer(); 
    String line = ""; 
    while ((line = rd.readLine()) != null) { 
     result.append(line); 
    } 
    // On certain android OS levels/certain hardware, this is not enough. 
    stream.close(); // This line is what is recommended in the documentation 

यहाँ क्या यह दस्तावेज में से पता चलता है:

cz.msebera.android.httpclient.HttpEntity 
@java.lang.Deprecated 
public abstract void consumeContent() 
          throws java.io.IOException 
This method is deprecated since version 4.1. Please use standard java 
convention to ensure resource deallocation by calling 
InputStream.close() on the input stream returned by getContent() 
संबंधित मुद्दे