قراءة Android من دفق الإدخال بكفاءة
-
21-09-2019 - |
سؤال
أقوم بتقديم طلب إلى موقع على موقع ويب لتطبيق 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);