कुशलता से एक इनपुट स्ट्रीम से Android पढ़ना


152

मैं जो वेबसाइट बना रहा हूं, उसके लिए मैं एक HTTP से वेबसाइट के लिए अनुरोध कर रहा हूं।

मैं एक DefaultHttpClient का उपयोग कर रहा हूं और अनुरोध जारी करने के लिए HttpGet का उपयोग कर रहा हूं। मुझे इकाई प्रतिक्रिया मिलती है और इससे पृष्ठ का html प्राप्त करने के लिए एक इनपुटस्ट्रीम ऑब्जेक्ट प्राप्त होता है।

मैं फिर उत्तर के रूप में निम्नानुसार चक्र:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";

while(x!= null){
total += x;
x = r.readLine();
}

हालाँकि यह भयावह रूप से धीमा है।

क्या यह अक्षम्य है? मैं एक बड़ा वेब पेज लोड नहीं कर रहा हूँ - www.cokezone.co.uk इसलिए फ़ाइल का आकार बड़ा नहीं है। क्या ऐसा करने के लिए इससे अच्छा तरीका है?

धन्यवाद

एंडी


जब तक आप वास्तव में लाइनों को पार्स नहीं कर रहे हैं तब तक यह लाइन द्वारा लाइन पढ़ने के लिए बहुत मायने नहीं रखता है। मैं बजाय निश्चित आकार बफ़र्स के माध्यम से चार द्वारा पढ़ा होगा: gist.github.com/fkirc/a231c817d582e114e791b77bb33e30e9
माइक76

जवाबों:


355

आपके कोड में समस्या यह है कि यह बहुत सारी भारी Stringवस्तुओं का निर्माण कर रहा है, उनकी सामग्री की नकल कर रहा है और उन पर कार्य कर रहा है। इसके बजाय, आपको प्रत्येक एपेंड पर StringBuilderनई Stringऑब्जेक्ट बनाने से बचने और चार सरणियों की प्रतिलिपि बनाने से बचने के लिए उपयोग करना चाहिए । आपके मामले का कार्यान्वयन कुछ इस तरह होगा:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
    total.append(line).append('\n');
}

अब आप totalइसे बिना रूपांतरित किए उपयोग कर सकते हैं String, लेकिन यदि आपको परिणाम की आवश्यकता है तो String, बस जोड़ें:

स्ट्रिंग परिणाम = Total.toString ();

मैं इसे बेहतर तरीके से समझाने की कोशिश करूँगा ...

  • a += b(या a = a + b), जहां aऔर bस्ट्रिंग्स हैं, दोनों की सामग्री की प्रतिलिपि बनाता है a और b एक नई वस्तु (ध्यान दें कि आप भी कॉपी कर रहे हैं a, जिसमें संचित होता है String), और आप प्रत्येक पुनरावृत्ति पर उन प्रतियों को कर रहे हैं।
  • a.append(b), जहां aएक है StringBuilder, सीधे bसामग्री को जोड़ देता है a, इसलिए आप प्रत्येक पुनरावृत्ति पर संचित स्ट्रिंग की प्रतिलिपि नहीं बनाते हैं।

23
बोनस पॉइंट्स के लिए, StringBuilder total = new StringBuilder(inputStream.available());
रियलब्लॉक

10
क्या यह नई पंक्ति वर्णों को नहीं काटता है?
नाथन श्वानमैन

5
इस तरह की कोशिश / पकड़ में लपेटने के लिए मत भूलना: {{((लाइन = r.readLine ())! = null) {total.append (लाइन) की कोशिश करें; }} कैच (IOException e) {Log.i (टैग, "इनपुटलाइन में रीडलाइन के साथ समस्या; फंक्शन स्ट्रिंग); }
बॉटबोट

4
@botbot: अपवाद को अनदेखा करना और उसकी अनदेखी करना केवल अपवाद को अनदेखा करने से बेहतर नहीं है ...
मत्ती वीरकुंकेन

50
यह आश्चर्यजनक है कि एंड्रॉइड में अंतर्निर्मित स्ट्रीम-टू-स्ट्रिंग रूपांतरण नहीं है। वेब पर हर कोड स्निपेट और ग्रह पर ऐप को फिर से लागू करना एक readlineलूप हास्यास्पद है। उस पैटर्न को 70 के दशक में मटर के हरे रंग के साथ मर जाना चाहिए था।
एडवर्ड ब्रे सेप

35

क्या आपने एक धारा को एक स्ट्रिंग में बदलने के लिए निर्मित विधि की कोशिश की है? यह अपाचे कॉमन्स लाइब्रेरी (org.apache.commons.io.IOUtils) का हिस्सा है।

तब आपका कोड यह एक पंक्ति होगा:

String total = IOUtils.toString(inputStream);

इसके लिए दस्तावेज यहां देखे जा सकते हैं: http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29

Apache Commons IO लाइब्रेरी को यहाँ से डाउनलोड किया जा सकता है: http://commons.apache.org/io/download_io.cgi


मुझे लगता है कि यह देर से प्रतिक्रिया है, लेकिन अभी Google खोज के माध्यम से इस पर ठोकर खाने के लिए हुआ है।
मकोतोसन

61
Android API
Charles Ma

2
ठीक है, यही कारण है कि मैंने बाहरी पुस्तकालय का उल्लेख किया है जो इसके पास है। मैंने अपने एंड्रॉइड प्रोजेक्ट में लाइब्रेरी को जोड़ा और धाराओं से पढ़ना आसान बना दिया।
मकोतोसन

मैं इसे कहां से डाउनलोड कर सकता हूं, और आपने इसे अपने Android प्रोजेक्ट में कैसे आयात किया?
सफारी

3
अगर आपको इसे डाउनलोड करना है, तो मैं इसे "बिल्ट इन" नहीं कहूंगा; फिर भी, मैंने अभी इसे डाउनलोड किया है, और इसे चलाऊंगा।
बी क्ले शैनन

15

अमरूद के साथ एक और संभावना:

निर्भरता: compile 'com.google.guava:guava:11.0.2'

import com.google.common.io.ByteStreams;
...

String total = new String(ByteStreams.toByteArray(inputStream ));

9

मेरा मानना ​​है कि यह काफी कुशल है ... एक InputStream से स्ट्रिंग प्राप्त करने के लिए, मैं निम्नलिखित विधि को कॉल करूंगा:

public static String getStringFromInputStream(InputStream stream) throws IOException
{
    int n = 0;
    char[] buffer = new char[1024 * 4];
    InputStreamReader reader = new InputStreamReader(stream, "UTF8");
    StringWriter writer = new StringWriter();
    while (-1 != (n = reader.read(buffer))) writer.write(buffer, 0, n);
    return writer.toString();
}

मैं हमेशा यूटीएफ -8 का उपयोग करता हूं। आप निश्चित रूप से, इनपुटस्ट्रीम के अलावा, एक तर्क के रूप में चारसेट सेट कर सकते हैं।


6

इस बारे में क्या। बेहतर प्रदर्शन देने के लिए लगता है।

byte[] bytes = new byte[1000];

StringBuilder x = new StringBuilder();

int numRead = 0;
while ((numRead = is.read(bytes)) >= 0) {
    x.append(new String(bytes, 0, numRead));
}

संपादित करें: वास्तव में यह स्टीलबाइट और मौरिस पेरी दोनों को शामिल करता है


समस्या यह है - मुझे पता नहीं है कि मैं शुरू होने से पहले पढ़ने वाली बात का आकार जानता हूं - इसलिए किसी न किसी रूप में अच्छी तरह से बढ़ रहे सरणी की आवश्यकता हो सकती है। जब तक आप बाइट ऐरे के आकार को ऑप्टिमाइज़ करने के लिए कितनी बड़ी बात नहीं कर लेते, यह जानने के लिए आप http के माध्यम से एक इनपुटस्ट्रीम या URL को क्वेरी कर सकते हैं। मुझे इसकी एक मोबाइल डिवाइस पर कुशल होना है जो मुख्य समस्या है! हालांकि उस विचार के लिए धन्यवाद - यह आज रात एक शॉट देगा और आपको बताएगा कि यह प्रदर्शन लाभ के मामले में कैसे संभालता है!
रेनेगेडएंडी

मुझे नहीं लगता कि आने वाली धारा का आकार इतना महत्वपूर्ण है। उपरोक्त कोड एक बार में 1000 बाइट्स पढ़ता है लेकिन आप उस आकार को बढ़ा / घटा सकते हैं। अपने परीक्षण के साथ यह इतना अंतर नहीं कर पाया कि मैंने 1000/10000 बाइट्स का उपयोग किया। यह सिर्फ एक सरल जावा ऐप था। मोबाइल डिवाइस पर यह अधिक महत्वपूर्ण हो सकता है।
एड्रियन

4
आप एक यूनिकोड इकाई के साथ समाप्त हो सकते हैं जो बाद के दो पाठों में कटा हुआ है। कुछ प्रकार के सीमा वर्ण तक पढ़ना बेहतर है, जैसे कि \ n, जो कि बफ़रड्रेडर वास्तव में करता है।
याकूब नॉर्डफ़ॉक

4

संभवतः Jaime Soriano के उत्तर की तुलना में कुछ अधिक तेज़, और एड्रियन के उत्तर की बहु-बाइट एन्कोडिंग समस्याओं के बिना, मेरा सुझाव है:

File file = new File("/tmp/myfile");
try {
    FileInputStream stream = new FileInputStream(file);

    int count;
    byte[] buffer = new byte[1024];
    ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream(stream.available());

    while (true) {
        count = stream.read(buffer);
        if (count <= 0)
            break;
        byteStream.write(buffer, 0, count);
    }

    String string = byteStream.toString();
    System.out.format("%d bytes: \"%s\"%n", string.length(), string);
} catch (IOException e) {
    e.printStackTrace();
}

क्या आप बता सकते हैं कि यह तेज क्यों होगा?
अखिल पिता

यह newline वर्णों के लिए इनपुट को स्कैन नहीं करता है, लेकिन सिर्फ 1024 बाइट्स का हिस्सा पढ़ता है। मैं यह तर्क नहीं दे रहा हूं कि इससे कोई व्यावहारिक फर्क पड़ेगा।
Heiner

@ रोनाल्ड जवाब पर कोई टिप्पणी? वह ऐसा ही कर रहा है, लेकिन इनपुटस्ट्रीम आकार के बराबर एक बड़ा हिस्सा है। यह भी अलग है कि अगर मैं निकोला उत्तर के रूप में बाइट सरणी के बजाय चार सरणी को स्कैन करूं तो क्या होगा? वास्तव में मैं सिर्फ यह जानना चाहता था कि किस मामले में कौन सा दृष्टिकोण सबसे अच्छा है? इसके अलावा readLine हटाता है \ n और r लेकिन मैंने Google io ऐप कोड को भी देखा है जो वे रीडलाइन का उपयोग कर रहे हैं
अखिल पिता

3

हो सकता है कि इसके बाद 'एक समय में एक पंक्ति' पढ़ें और तार से जुड़ें, कोशिश करें कि 'सभी उपलब्ध पढ़ें' ताकि लाइन के अंत तक स्कैनिंग से बचा जा सके और स्ट्रिंग जॉइन से भी बचा जा सके।

यानी, InputStream.available()औरInputStream.read(byte[] b), int offset, int length)


हम्म। तो यह इस तरह होगा: int ऑफसेट = 5000; बाइट [] bArr = नई बाइट [100]; बाइट [] कुल = बाइट [5000]; जबकि (InputStream.available) {ऑफसेट = InputStream.read (bArr, ऑफसेट, 100); for (int i = 0; मैं <ऑफसेट; i ++) {कुल [i] = bArr [i]; } bArr = नया बाइट [100]; } क्या यह वास्तव में अधिक कुशल है - या मैंने इसे बुरी तरह से लिखा है! कृपया एक उदाहरण दें!
रेनेगेडएंडी

2
नहीं नहीं नहीं नहीं, मेरा मतलब केवल {बाइट कुल [] = नया [instrm.available ()]; instrm.read (कुल, 0, total.length); } और यदि आपको इसके बाद एक स्ट्रिंग के रूप में इसकी आवश्यकता थी, {स्ट्रिंग asString = स्ट्रिंग (कुल, 0, कुल ।length, "utf-8") का उपयोग करें; // मान लीजिए utf8 :-)}
स्टीलबाइट्स

2

एक समय में पाठ की एक पंक्ति पढ़ना, और कहा जाता है कि एक स्ट्रिंग को अलग-अलग करने के लिए लाइन अलग-अलग समय है और दोनों प्रत्येक लाइन और इतने सारे विधि चालान के उपरि निकालने में समय लेने वाली हैं।

मैं स्ट्रीम डेटा को रखने के लिए एक सभ्य आकार के बाइट सरणी को आवंटित करके बेहतर प्रदर्शन प्राप्त करने में सक्षम था, और जो कि जरूरत पड़ने पर एक बड़े सरणी के साथ बदल दिया जाता है, और जितना संभव हो उतना पढ़ने की कोशिश कर सकता है।

किसी कारण से, एंड्रॉइड बार-बार पूरी फाइल को डाउनलोड करने में विफल रहा जब कोड का उपयोग HTTPStrlConnection द्वारा दिए गए InputStream का उपयोग किया गया था, इसलिए मुझे यह सुनिश्चित करने के लिए कि मुझे पूरी फ़ाइल मिलेगी या रद्द करना है, दोनों के लिए एक बफर-राइडर और एक हाथ से चलने वाले टाइमआउट तंत्र का उपयोग करना होगा। तबादला।

private static  final   int         kBufferExpansionSize        = 32 * 1024;
private static  final   int         kBufferInitialSize          = kBufferExpansionSize;
private static  final   int         kMillisecondsFactor         = 1000;
private static  final   int         kNetworkActionPeriod        = 12 * kMillisecondsFactor;

private String loadContentsOfReader(Reader aReader)
{
    BufferedReader  br = null;
    char[]          array = new char[kBufferInitialSize];
    int             bytesRead;
    int             totalLength = 0;
    String          resourceContent = "";
    long            stopTime;
    long            nowTime;

    try
    {
        br = new BufferedReader(aReader);

        nowTime = System.nanoTime();
        stopTime = nowTime + ((long)kNetworkActionPeriod * kMillisecondsFactor * kMillisecondsFactor);
        while(((bytesRead = br.read(array, totalLength, array.length - totalLength)) != -1)
        && (nowTime < stopTime))
        {
            totalLength += bytesRead;
            if(totalLength == array.length)
                array = Arrays.copyOf(array, array.length + kBufferExpansionSize);
            nowTime = System.nanoTime();
        }

        if(bytesRead == -1)
            resourceContent = new String(array, 0, totalLength);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        if(br != null)
            br.close();
    }
    catch(IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

संपादित करें: यह पता चला है कि अगर आपको सामग्री को फिर से एन्कोडेड करने की आवश्यकता नहीं है (यानी, आप आईएस के रूप में सामग्री चाहते हैं ) तो आपको किसी भी पाठक उपवर्ग का उपयोग नहीं करना चाहिए। बस उपयुक्त स्ट्रीम उपवर्ग का उपयोग करें।

पूर्ववर्ती विधि की शुरुआत को 2 से 3 बार अतिरिक्त गति देने के लिए निम्नलिखित पंक्तियों के साथ बदलें ।

String  loadContentsFromStream(Stream aStream)
{
    BufferedInputStream br = null;
    byte[]              array;
    int                 bytesRead;
    int                 totalLength = 0;
    String              resourceContent;
    long                stopTime;
    long                nowTime;

    resourceContent = "";
    try
    {
        br = new BufferedInputStream(aStream);
        array = new byte[kBufferInitialSize];

यह उपरोक्त और स्वीकृत उत्तरों की तुलना में बहुत तेज है। आप एंड्रॉइड पर "रीडर" और "स्ट्रीम" का उपयोग कैसे करते हैं?
स्टीवजीएसडी

1

यदि फ़ाइल लंबी है, तो आप प्रत्येक पंक्ति के लिए स्ट्रिंग संयोजन का उपयोग करने के बजाय StringBuilder में संलग्न करके अपने कोड को अनुकूलित कर सकते हैं।


यह ईमानदार होने के लिए लंबे समय तक नहीं - वेबसाइट www.cokezone.co.uk के पेज स्रोत - तो वास्तव में इतना बड़ा नहीं है। निश्चित रूप से 100kb से कम है।
रेनेगेडे एंडी

क्या किसी के पास इस बारे में कोई अन्य विचार है कि इसे और अधिक कुशल कैसे बनाया जा सकता है - या अगर यह भी अक्षम है !? यदि उत्तरार्द्ध सत्य है - तो यह एसओ लंबा क्यों लगता है? मुझे नहीं लगता कि कनेक्शन को दोष देना है।
रेनेगेडेएंडी

1
    byte[] buffer = new byte[1024];  // buffer store for the stream
    int bytes; // bytes returned from read()

    // Keep listening to the InputStream until an exception occurs
    while (true) {
        try {
            // Read from the InputStream
            bytes = mmInStream.read(buffer);

            String TOKEN_ = new String(buffer, "UTF-8");

            String xx = TOKEN_.substring(0, bytes);

1

InputStream को String में बदलने के लिए हम BufferedReader.readLine () विधि का उपयोग करते हैं । हम बफ़रड्रेडर वापसी नल तक इसका पुनरावृत्ति करते हैं, जिसका अर्थ है कि पढ़ने के लिए अधिक डेटा नहीं है। प्रत्येक पंक्ति StringBuilder से जुड़ी होगी और स्ट्रिंग के रूप में वापस आ जाएगी।

 public static String convertStreamToString(InputStream is) {

        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();
    }
}`

और अंत में किसी भी वर्ग से जहां आप कॉल फ़ंक्शन को कनवर्ट करना चाहते हैं

String dataString = Utils.convertStreamToString(in);

पूर्ण


-1

मैं पूरा डेटा पढ़ने के लिए उपयोग कर रहा हूँ:

// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.