سؤال

أقوم بتقديم طلب إلى موقع على موقع ويب لتطبيق Android الذي أقوم به.

أنا أستخدم DefaultHttpClient واستخدام httpget لإصدار الطلب. أحصل على استجابة الكيان ومن هذا الحصول على كائن inputstream للحصول على 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 وبالتالي فإن حجم الملف ليس كبيرًا. هل هناك طريقة أفضل للقيام بذلك؟

شكرًا

أندي

هل كانت مفيدة؟

المحلول

المشكلة في الكود الخاص بك هي أنها تخلق الكثير من الثقيلة String الكائنات ، نسخ محتوياتها وأداء العمليات عليها. بدلاً من ذلك ، يجب أن تستخدم StringBuilder لتجنب إنشاء جديد String الكائنات على كل إلحاح وتجنب نسخ صفائف char. سيكون تنفيذ قضيتك شيئًا من هذا القبيل:

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, ، لذلك لا تقوم بنسخ السلسلة المتراكمة في كل تكرار.

نصائح أخرى

هل جربت الطريقة المضمنة لتحويل دفق إلى سلسلة؟ إنه جزء من مكتبة Apache Commons (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

احتمال آخر مع الجوافة:

الاعتماد: compile 'com.google.guava:guava:11.0.2'

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

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

أعتقد أن هذا فعال بما فيه الكفاية ... للحصول على سلسلة من 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();
}

أنا دائما استخدام UTF-8. يمكنك ، بالطبع ، تعيين Charset كوسيطة ، إلى جانب InputStream.

ماذا عن هذا. يبدو أنه يعطي أداء أفضل.

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

تحرير: في الواقع يشمل هذا النوع من الصلب وموريس بيري

ربما أسرع إلى حد ما من إجابة خايمي سوريانو ، وبدون مشاكل ترميز بايت متعددة في إجابة أدريان ، أقترح:

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

ربما بدلاً من ذلك ، ثم اقرأ "سطر واحد في كل مرة" وانضم إلى الأوتار ، حاول "قراءة كل شيء متاح" لتجنب المسح الضوئي لنهاية الخط ، ولتجنب صلات السلسلة.

بمعنى آخر، InputStream.available() و InputStream.read(byte[] b), int offset, int length)

إن قراءة سطر واحد من النص في وقت واحد ، وإلحاق الخط المذكور إلى سلسلة بشكل فردي يستغرق وقتًا طويلاً في استخراج كل سطر ونفقات كبيرة للعديد من دعوات الطريقة.

تمكنت من الحصول على أداء أفضل من خلال تخصيص مجموعة بايت ذات حجم لائق للاحتفاظ ببيانات الدفق ، والتي يتم استبدالها بشكل متكرر بمجموعة أكبر عند الحاجة ، ومحاولة قراءة الصفيف بقدر ما يمكن أن يحملها الصفيف.

لسبب ما ، فشل Android مرارًا وتكرارًا في تنزيل الملف بأكمله عندما يستخدم الكود جهاز inputstream الذي تم إرجاعه بواسطة HttPurlConnection ، لذلك اضطررت إلى اللجوء إلى استخدام كل من المخزن المؤقت وآلية مهلة مدفوعة باليد لضمان إما الحصول على الملف أو الإلغاء بأكمله التحويل.

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];

إذا كان الملف طويلًا ، فيمكنك تحسين التعليمات البرمجية الخاصة بك عن طريق إلحاق stringBuilder بدلاً من استخدام سلسلة متسلسلة لكل سطر.

    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);

لتحويل inputStream إلى سلسلة نستخدمها BufferedReader.Readline () طريقة. نحن نتكرر حتى BufferedReader إرجاع NULL مما يعني أنه لا يوجد المزيد من البيانات للقراءة. سيتم إلحاق كل سطر بـ 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);

مكتمل

أنا أستخدم قراءة البيانات الكاملة:

// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top