Android Reading from an Input stream efficiently
The problem in your code is that it's creating lots of heavy String
objects, copying their contents and performing operations on them. Instead, you should use StringBuilder
to avoid creating new String
objects on each append and to avoid copying the char arrays. The implementation for your case would be something like this:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
You can now use total
without converting it to String
, but if you need the result as a String
, simply add:
String result = total.toString();
I'll try to explain it better...
a += b
(ora = a + b
), wherea
andb
are Strings, copies the contents of botha
andb
to a new object (note that you are also copyinga
, which contains the accumulatedString
), and you are doing those copies on each iteration.a.append(b)
, wherea
is aStringBuilder
, directly appendsb
contents toa
, so you don't copy the accumulated string at each iteration.
How do an InputStream, InputStreamReader and BufferedReader work together in Java?
This Streams in Java concepts and usage link, give a very nice explanations.
Streams, Readers, Writers, BufferedReader, BufferedWriter – these are the terminologies you will deal with in Java. There are the classes provided in Java to operate with input and output. It is really worth to know how these are related and how it is used. This post will explore the Streams in Java and other related classes in detail. So let us start:
Let us define each of these in high level then dig deeper.
Streams
Used to deal with byte level data
Reader/Writer
Used to deal with character level. It supports various character encoding also.
BufferedReader/BufferedWriter
To increase performance. Data to be read will be buffered in to memory for quick access.
While these are for taking input, just the corresponding classes exists for output as well. For example, if there is an InputStream that is meant to read stream of byte, and OutputStream will help in writing stream of bytes.
InputStreams
There are many types of InputStreams java provides. Each connect to distinct data sources such as byte array, File etc.
For example FileInputStream connects to a file data source and could be used to read bytes from a File. While ByteArrayInputStream could be used to treat byte array as input stream.
OutputStream
This helps in writing bytes to a data source. For almost every InputStream there is a corresponding OutputStream, wherever it makes sense.
UPDATE
What is Buffered Stream?
Here I'm quoting from Buffered Streams, Java documentation (With a technical explanation):
Buffered Streams
Most of the examples we've seen so far use unbuffered I/O. This means
each read or write request is handled directly by the underlying OS.
This can make a program much less efficient, since each such request
often triggers disk access, network activity, or some other operation
that is relatively expensive.To reduce this kind of overhead, the Java platform implements buffered
I/O streams. Buffered input streams read data from a memory area known
as a buffer; the native input API is called only when the buffer is
empty. Similarly, buffered output streams write data to a buffer, and
the native output API is called only when the buffer is full.
Sometimes I'm losing my hair reading a technical documentation. So, here I quote the more humane explanation from https://yfain.github.io/Java4Kids/:
In general, disk access is much slower than the processing performed
in memory; that’s why it’s not a good idea to access the disk a
thousand times to read a file of 1,000 bytes. To minimize the number
of times the disk is accessed, Java provides buffers, which serve as
reservoirs of data.In reading File with FileInputStream then BufferedInputStream, the
class BufferedInputStream works as a middleman between FileInputStream
and the file itself. It reads a big chunk of bytes from a file into
memory (a buffer) in one shot, and the FileInputStream object then
reads single bytes from there, which are fast memory-to-memory
operations. BufferedOutputStream works similarly with the class
FileOutputStream.The main idea here is to minimize disk access. Buffered streams are
not changing the type of the original streams — they just make reading
more efficient. A program performs stream chaining (or stream piping)
to connect streams, just as pipes are connected in plumbing.
Proper way to make an input stream keep reading until program closes?
If I understand right, you issue is to stop reading from the InputStream
. You can use a volatile boolean variable to stop reading:
class PollingRunnable implements Runnable{
private static final String TAG = PollingRunnable.class.getSimpleName();
private InputStream inputStream;
private volatile boolean shouldKeepPolling = true;
public PollingRunnable(InputStream inputStream) {
this.inputStream = inputStream;
}
public void stopPolling() {
shouldKeepPolling = false;
}
@Override
public void run() {
while (shouldKeepPolling) {
final byte[] buffer = new byte[256];
int bytesRead = 0;
try {
bytesRead = inputStream.read(buffer, 0, buffer.length);
String fullResponse = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
//Process response
} catch (IOException e) {
Log.e(TAG, "Exception while polling input stream! ", e);
} finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException e1) {
Log.e(TAG, "Exception while closing input stream! ", e1);
}
}
}
}
}
}
To stop polling just use:
// Supply you input stream here
PollingRunnable pollingRunnable = new PollingRunnable(inputStream);
new Thread(pollingRunnable).start();
//To stop polling
pollingRunnable.stopPolling();
Android - Whats the most efficient way to read an inputstream and save as file?
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int remaining = length;
int read = 0;
while (remaining > 0
&& (read = in.read(buffer, 0, Math.min(remaining, bufferSize))) >= 0) {
out.write(buffer, 0, read);
remaining -= read;
}
Note that the above makes sure you don't write more that length bytes. But it doesn't make sure you write exactly length bytes. I don't see how you could do this without reading length bytes in memory, or reading length bytes and writing to a temp file, then writing the temp file to the final destination.
Seeking on an InputStream in Android - Java
Here's one approach worth trying that worked for me. The idea is to cast InputStream to FileInputStream by getting AssetFileDescriptor, and then getting an instance of FileChannel that lets you seek back and forth.
long position = 0, bytesRead = 0, currentRead = 0;
Context context = getApplicationContext();
ContentResolver cr = context.getContentResolver();
InputStream is = cr.openInputStream(fileUri);
AssetFileDescriptor assetFileDescriptor = null;
FileInputStream fis = null;
FileChannel fc = null;
try {
assetFileDescriptor = getContentResolver().openAssetFileDescriptor(fileUri[0], "r");
is = cr.openInputStream(fileUri[0]);
Utility.sendFileInfo(is, out, fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fis = new FileInputStream(assetFileDescriptor.getFileDescriptor());;
fc = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(Utility.BUFFER_SIZE);
while(position < filesize){
try {
fc.position(position);
currentRead = fc.read(buffer);
//Use the populated buffer to send data out
SendDatagramPacket(buffer, 0, currentRead)
bytesRead += currentRead;
position = bytesRead;
buffer = ByteBuffer.allocate(Utility.BUFFER_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
How do I read / convert an InputStream into a String in Java?
A nice way to do this is using Apache commons IOUtils
to copy the InputStream
into a StringWriter
... something like
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();
or even
// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding);
Alternatively, you could use ByteArrayOutputStream
if you don't want to mix your Streams and Writers
Java InputStream . Is there is Correct And Efficient way?
Using only standard API, the DataInputStream
class has a method called readFully
that fills a byte array from the stream:
byte[] data = new byte[lengthtoread];
DataInputStream in = new DataInputStream(mmInStream);
in.readFully(data);
Don't forget to close the streams when you are done with them!
Related Topics
How to Set Android Lock Screen Image
Android - Setting a Timeout for an Asynctask
Inputstream.Available() Is 0 Always
Picking a Random Element from a Set
How to Move My Jmenubar to the Screen Menu Bar on MAC Os X
What's the Syntax to Import a Class in a Default Package in Java
Spring Boot + JPA:Column Name Annotation Ignored
How to Load a Resource from Web-Inf Directory of a Web Archive
How to Find the Currently Running Applications Programmatically in Android
Best Way to Compare Dates in Android
Namenode: Java.Net.Bindexception
Why Does Priorityqueue.Tostring Return the Wrong Element Order
What's the Syntax for Mod in Java
Why Is the Max Recursion Depth I Can Reach Non-Deterministic